Grotti 3.2.0: Jetzt-Liste, Filament-Pause, Strom-Torte, UI-Polish.
Jetzt/Topbar und Material-Routing, Pause bei Filament-Ende, Druckbericht mit Stromanteilen, Design- und Kamera-Stream-Polish; Bedarf-Alert-Spam und Platte-belegt-Bugs behoben.
This commit is contained in:
1 parent
6e59df22e5
commit
4ddffbbeb1
68 files changed
+4348
-1215
No files matched your search
@@ -5,7 +5,16 @@
|
||||
<h1>Kameras</h1>
|
||||
<p class="tiny">Livebilder aller Drucker.</p>
|
||||
</div>
|
||||
<span id="when"></span>
|
||||
<div class="page-head-side">
|
||||
<label class="field cam-mode-field">
|
||||
<span class="tiny">Bild</span>
|
||||
<select id="cam-mode" title="Kamera-Modus">
|
||||
<option value="snapshot">Snapshot</option>
|
||||
<option value="stream">Stream</option>
|
||||
</select>
|
||||
</label>
|
||||
<span id="when"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="camera-grid" id="grid"></div>
|
||||
{% endblock %}
|
||||
@@ -13,10 +22,27 @@
|
||||
<script>
|
||||
const tiles = new Map();
|
||||
|
||||
(function initCamMode() {
|
||||
const sel = document.getElementById("cam-mode");
|
||||
if (!sel || typeof cameraMode !== "function") return;
|
||||
sel.value = cameraMode();
|
||||
sel.addEventListener("change", () => {
|
||||
setCameraMode(sel.value);
|
||||
for (const tile of tiles.values()) {
|
||||
const img = tile.querySelector('[data-role="cam"]');
|
||||
if (img) img.dataset.srcKey = "";
|
||||
}
|
||||
refresh().catch(() => {});
|
||||
});
|
||||
window.addEventListener("grotti-camera-mode", () => {
|
||||
sel.value = cameraMode();
|
||||
});
|
||||
})();
|
||||
|
||||
function metaLine(printer) {
|
||||
const state = shownState(printer);
|
||||
const busy = state === "printing" || state === "paused";
|
||||
const bits = [STATE_LABELS[state] || state];
|
||||
const bits = [chipHtml(state, STATE_LABELS[state] || state)];
|
||||
if (busy) {
|
||||
const pct = Math.round((printer.progress || 0) * 100);
|
||||
bits.push(pct + " %");
|
||||
@@ -25,7 +51,7 @@ function metaLine(printer) {
|
||||
else if (hours(printer.remain)) bits.push("Rest " + hours(printer.remain));
|
||||
}
|
||||
const job = (printer.filename || "").replace(/\.gcode$/i, "").replace(/^(\d+-)+/, "");
|
||||
if (busy && job) bits.push(job);
|
||||
if (busy && job) bits.push(escapeHtml(job));
|
||||
return bits.join(" · ");
|
||||
}
|
||||
|
||||
@@ -61,15 +87,16 @@ async function refresh() {
|
||||
return;
|
||||
}
|
||||
if (grid.querySelector(".empty-state")) grid.innerHTML = "";
|
||||
const streaming = cameraMode() === "stream";
|
||||
const seen = new Set();
|
||||
for (const printer of printers) {
|
||||
seen.add(printer.host);
|
||||
const tile = ensureTile(printer);
|
||||
const state = shownState(printer);
|
||||
tile.className = "camera-tile " + state;
|
||||
tile.className = "camera-tile " + state + (streaming ? " stream" : "");
|
||||
tile.querySelector(".cam-name").textContent = printer.name;
|
||||
tile.querySelector(".host").textContent = printer.host;
|
||||
tile.querySelector(".cam-meta").textContent = metaLine(printer);
|
||||
tile.querySelector(".cam-meta").innerHTML = metaLine(printer);
|
||||
tile.querySelector(".cam-open").href = "/#" + encodeURIComponent(printer.host);
|
||||
const img = tile.querySelector('[data-role="cam"]');
|
||||
const empty = tile.querySelector('[data-role="cam-empty"]');
|
||||
@@ -78,10 +105,13 @@ async function refresh() {
|
||||
img.hidden = true;
|
||||
empty.hidden = false;
|
||||
empty.innerHTML = icon("camera") + "<span>Keine Kamera</span>";
|
||||
img.dataset.srcKey = "";
|
||||
continue;
|
||||
}
|
||||
img.alt = "Kamera " + printer.name;
|
||||
const next = withToken(printer.camera) + "&t=" + Math.floor(Date.now() / 2000);
|
||||
const next = cameraUrl(printer, streaming ? 0 : 2000);
|
||||
if (img.dataset.srcKey === next) continue;
|
||||
img.dataset.srcKey = next;
|
||||
img.onerror = () => {
|
||||
img.hidden = true;
|
||||
empty.hidden = false;
|
||||
|
||||
Reference in new issue
Block a user