diff --git a/web/themes/default/app.js b/web/themes/default/app.js index c34e172..781d122 100644 --- a/web/themes/default/app.js +++ b/web/themes/default/app.js @@ -277,8 +277,49 @@ function initPrinters(){ var idx=window._printerIndex||1; _activePrinter=_printers.find(function(p){return String(p.id)===String(idx)})||_printers[0]||null; renderPrinterDropdown(); + _initDashboardPowerCard(); }).catch(function(){}); } + +// ── Dashboard Power Switch (Issue: hard to find + poorly visible in the +// Printers tab; mirrors togglePrinterPower()/​_refreshPrinterPowerIcon() for +// the active printer, on a second, more prominent toggle on the Dashboard) ── +function _initDashboardPowerCard(){ + var card=document.getElementById('card-power'); + if(!card)return; + if(!_activePrinter||!_activePrinter.has_power_control){card.style.display='none';return;} + card.style.display=''; + _refreshDashboardPower(); +} +function _refreshDashboardPower(){ + if(!_activePrinter||!_activePrinter.has_power_control)return; + var bridgeUrl=(_activePrinter.bridge_url||'').replace(/\/+$/,''); + fetch(bridgeUrl+'/kx/printers/'+encodeURIComponent(_activePrinter.id)+'/power-status',{signal:AbortSignal.timeout(5000)}) + .then(function(r){return r.json()}) + .then(function(d){ + var onBtn=document.getElementById('d-power-on'), offBtn=document.getElementById('d-power-off'); + if(!onBtn||!offBtn)return; + onBtn.classList.toggle('spd-active',d.state==='on'); + offBtn.classList.toggle('spd-active',d.state==='off'); + }) + .catch(function(){/* status endpoint optional - toggle just stays neutral */}); +} +function setDashboardPower(action){ + if(!_activePrinter)return; + if(action==='off'&&!confirm(T.printers_power_off_confirm||'Turn printer power off? Make sure no print is running.'))return; + var onBtn=document.getElementById('d-power-on'), offBtn=document.getElementById('d-power-off'); + if(onBtn)onBtn.style.opacity='0.5'; + if(offBtn)offBtn.style.opacity='0.5'; + post('/kx/printers/'+encodeURIComponent(_activePrinter.id)+'/power',{action:action}).then(function(){ + if(onBtn)onBtn.style.opacity='1'; + if(offBtn)offBtn.style.opacity='1'; + setTimeout(_refreshDashboardPower,1500); + }).catch(function(e){ + if(onBtn)onBtn.style.opacity='1'; + if(offBtn)offBtn.style.opacity='1'; + clog('Power-Fehler: '+e,'msg-err'); + }); +} function renderPrinterDropdown(){ var wrap=document.getElementById('printer-dropdown-wrap'); var single=document.getElementById('h-pname-single'); @@ -367,6 +408,9 @@ function applyLang(){ setText('d-card-temps',T.card_temps); setText('d-card-lightfan',T.card_light_fan); setText('d-card-speed',T.card_speed); + setText('d-card-power',T.card_power); + setText('d-power-lbl-on',T.printers_power_on_short||'An'); + setText('d-power-lbl-off',T.printers_power_off_short||'Aus'); setText('d-card-cam',T.card_cam); setText('d-card-ams',T.panel_ams_title); setText('d-lbl-elapsed',T.lbl_elapsed); @@ -2025,6 +2069,7 @@ var pollTimer; }).catch(function(){}); poll();pollTimer=setInterval(poll,ms); setInterval(_loadSpoolmanStatus,30000); + setInterval(_refreshDashboardPower,30000); // initDashGrid() is called at the very end of the file, after all the // DASH_* var declarations below have executed (they are hoisted but not yet // assigned at this point in the IIFE). @@ -2041,7 +2086,7 @@ var pollTimer; // - columnOpts.breakpoints -> 1-column below 700px GRID width (sidebar-aware) var DASH_STORAGE_KEY='dashLayout'; var DASH_LAYOUT_VERSION=3; // v1/v2 = older editors; discard those states -var DASH_CARD_KEYS=['camera','progress','temps','motion','speed','fan','ams']; +var DASH_CARD_KEYS=['camera','progress','temps','motion','speed','power','fan','ams']; // Layout arrays in GridStack save()/load() format. cellHeight=60px. var DASH_DEFAULT_LAYOUT=[ {id:'camera', x:0,y:0, w:12,h:7}, @@ -2049,8 +2094,9 @@ var DASH_DEFAULT_LAYOUT=[ {id:'temps', x:0,y:13,w:6, h:7}, {id:'motion', x:6,y:13,w:6, h:7}, {id:'speed', x:0,y:20,w:6, h:3}, - {id:'fan', x:6,y:20,w:6, h:3}, - {id:'ams', x:0,y:23,w:12,h:4}, + {id:'power', x:6,y:20,w:6, h:3}, + {id:'fan', x:0,y:23,w:6, h:3}, + {id:'ams', x:0,y:26,w:12,h:4}, ]; // "Wide desktop" preset (Blaim, Issue #89): big camera left, settings center, // motion right. @@ -2128,6 +2174,20 @@ function initDashGrid(){ var item=_dashItem(n.id); if(item){_dashGrid.removeWidget(item,false);item.style.display='none';} }); + // Cards added after a user's layout was already saved (e.g. the "power" + // card) never appear otherwise: load(...,false) only updates ids already + // present in the saved layout, it never adds new ones. Append any missing + // known card next to its default-layout neighbour instead of silently + // dropping it - the alternative (bumping DASH_LAYOUT_VERSION) would wipe + // every user's custom layout just to surface one new card. + var known={}; + state.layout.forEach(function(n){known[n.id]=true;}); + _dashHidden.forEach(function(n){known[n.id]=true;}); + DASH_DEFAULT_LAYOUT.forEach(function(def){ + if(known[def.id])return; + var item=_dashItem(def.id); + if(item)_dashGrid.addWidget(item,{x:def.x,y:def.y,w:def.w,h:def.h}); + }); }else{ _dashGrid.load(DASH_DEFAULT_LAYOUT,false); } diff --git a/web/themes/default/index.html b/web/themes/default/index.html index c12fe1d..2c1c5e4 100644 --- a/web/themes/default/index.html +++ b/web/themes/default/index.html @@ -335,6 +335,21 @@ + + +
🌀 Lüfter
diff --git a/web/translations/de.json b/web/translations/de.json index d134c8b..9c79a0d 100644 --- a/web/translations/de.json +++ b/web/translations/de.json @@ -68,6 +68,7 @@ "card_light_fan": "Lüfter", "card_progress": "Fortschritt", "card_speed": "Druckgeschwindigkeit", + "card_power": "Steckdose", "card_temps": "Temperaturen", "confirm_cancel": "Druck wirklich abbrechen?", "dash_done": "Fertig", @@ -228,6 +229,8 @@ "printers_power_on": "Strom: An", "printers_power_off": "Strom: Aus", "printers_power_off_confirm": "Drucker-Strom ausschalten? Stelle sicher, dass kein Druck läuft.", + "printers_power_on_short": "An", + "printers_power_off_short": "Aus", "printers_remove_confirm": "Drucker \"{name}\" entfernen? Die Bridge startet neu.", "printers_switch": "Wechseln →", "progress_action_clear": "Leeren", diff --git a/web/translations/en.json b/web/translations/en.json index c53651a..0f34eda 100644 --- a/web/translations/en.json +++ b/web/translations/en.json @@ -68,6 +68,7 @@ "card_light_fan": "Fan", "card_progress": "Progress", "card_speed": "Print Speed", + "card_power": "Power Switch", "card_temps": "Temperatures", "confirm_cancel": "Really cancel the print?", "dash_done": "Done", @@ -228,6 +229,8 @@ "printers_power_on": "Power: On", "printers_power_off": "Power: Off", "printers_power_off_confirm": "Turn printer power off? Make sure no print is running.", + "printers_power_on_short": "On", + "printers_power_off_short": "Off", "printers_remove_confirm": "Remove printer \"{name}\"? The bridge will restart.", "printers_switch": "Switch →", "progress_action_clear": "Clear", diff --git a/web/translations/es.json b/web/translations/es.json index da746c2..f6675c1 100644 --- a/web/translations/es.json +++ b/web/translations/es.json @@ -68,6 +68,7 @@ "card_light_fan": "Ventilador", "card_progress": "Progreso", "card_speed": "Velocidad de impresión", + "card_power": "Enchufe", "card_temps": "Temperaturas", "confirm_cancel": "¿Realmente cancelar la impresión?", "dash_done": "Listo", @@ -228,6 +229,8 @@ "printers_power_on": "Alimentación: Encendida", "printers_power_off": "Alimentación: Apagada", "printers_power_off_confirm": "¿Apagar la alimentación de la impresora? Asegúrate de que no haya ninguna impresión en curso.", + "printers_power_on_short": "Encendido", + "printers_power_off_short": "Apagado", "printers_remove_confirm": "¿Eliminar impresora \"{name}\"? El bridge se reiniciará.", "printers_switch": "Cambiar →", "progress_action_clear": "Vaciar", diff --git a/web/translations/fr.json b/web/translations/fr.json index cd4da1d..6ef3926 100644 --- a/web/translations/fr.json +++ b/web/translations/fr.json @@ -68,6 +68,7 @@ "card_light_fan": "Ventilateur", "card_progress": "Progression", "card_speed": "Vitesse d'impression", + "card_power": "Prise électrique", "card_temps": "Températures", "confirm_cancel": "Vraiment annuler l'impression ?", "fd_cancel": "Annuler", @@ -216,6 +217,8 @@ "printers_power_on": "Alimentation : Allumée", "printers_power_off": "Alimentation : Éteinte", "printers_power_off_confirm": "Éteindre l'alimentation de l'imprimante ? Assurez-vous qu'aucune impression n'est en cours.", + "printers_power_on_short": "Marche", + "printers_power_off_short": "Arrêt", "printers_remove_confirm": "Supprimer l'imprimante \"{name}\" ? Le bridge va redémarrer.", "printers_switch": "Changer →", "progress_action_clear": "Vider", diff --git a/web/translations/it.json b/web/translations/it.json index 2e090bc..e88041e 100644 --- a/web/translations/it.json +++ b/web/translations/it.json @@ -68,6 +68,7 @@ "card_light_fan": "Ventola", "card_progress": "Avanzamento", "card_speed": "Velocità di stampa", + "card_power": "Presa di corrente", "card_temps": "Temperature", "confirm_cancel": "Annullare davvero la stampa?", "fd_cancel": "Annulla", @@ -216,6 +217,8 @@ "printers_power_on": "Alimentazione: Accesa", "printers_power_off": "Alimentazione: Spenta", "printers_power_off_confirm": "Spegnere l'alimentazione della stampante? Assicurati che non sia in corso alcuna stampa.", + "printers_power_on_short": "Acceso", + "printers_power_off_short": "Spento", "printers_remove_confirm": "Rimuovere la stampante \"{name}\"? Il bridge si riavvierà.", "printers_switch": "Cambia →", "progress_action_clear": "Cancella", diff --git a/web/translations/zh-cn.json b/web/translations/zh-cn.json index 54e1114..3866843 100644 --- a/web/translations/zh-cn.json +++ b/web/translations/zh-cn.json @@ -68,6 +68,7 @@ "card_light_fan": "风扇", "card_progress": "进度", "card_speed": "打印速度", + "card_power": "电源插座", "card_temps": "温度", "confirm_cancel": "确定要取消打印吗?", "dash_done": "完成", @@ -228,6 +229,8 @@ "printers_power_on": "电源:开", "printers_power_off": "电源:关", "printers_power_off_confirm": "关闭打印机电源?请确认当前没有正在进行的打印任务。", + "printers_power_on_short": "开", + "printers_power_off_short": "关", "printers_remove_confirm": "移除打印机 \"{name}\"? Bridge 将重启。", "printers_switch": "切换 →", "progress_action_clear": "清除",