New web UI with iPhone web app
Redesigned status, log, tag generator and settings pages: spool rings in filament colour, live job card (progress and time left read from CC2 Control's /api/printer while a job runs), log page with filters, iOS-style settings with a write/dry-run switch. On phones: bottom tab bar, printer switcher, slot details in a bottom sheet. Dark mode follows the system. Web app manifest and home screen icons for "Add to Home Screen". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014GcEk5tkfFSYD6NTF66NkC
This commit is contained in:
1 parent
67c3b54903
commit
ae44441cfb
16 files changed
+982
-285
No files matched your search
@@ -198,6 +198,7 @@ class PrinterWorker:
|
||||
self.tray_status: dict[int, int] = {}
|
||||
self.record: dict | None = None
|
||||
self.cc2_data: dict | None = None
|
||||
self.print_info: dict | None = None # CC2 Control /api/printer, only read while a job runs
|
||||
|
||||
@property
|
||||
def id(self) -> str:
|
||||
@@ -250,6 +251,12 @@ class PrinterWorker:
|
||||
self._error("cc2", None)
|
||||
except CC2Error as e:
|
||||
self._error("cc2", str(e))
|
||||
self.print_info = None
|
||||
if self.cc2_data is not None and (self.cc2_data.get("job") or {}).get("active"):
|
||||
try:
|
||||
self.print_info = self.cc2.printer()
|
||||
except CC2Error:
|
||||
pass # progress is only shown in the UI; the spool list above is what counts
|
||||
if self.record is not None:
|
||||
self._process_record(self.record)
|
||||
self._process_scans()
|
||||
@@ -693,9 +700,31 @@ class PrinterWorker:
|
||||
"cc2_enabled": None if cc2 is None else bool(cc2.get("enabled")),
|
||||
"job_active": None if cc2 is None else bool((cc2.get("job") or {}).get("active")),
|
||||
"host_key": self.bridge.keys.host_key(self.id) is not None,
|
||||
"job": self._job_summary(),
|
||||
"slots": slots,
|
||||
}
|
||||
|
||||
def _job_summary(self) -> dict | None:
|
||||
job = (self.cc2_data or {}).get("job") or {}
|
||||
if not job.get("active"):
|
||||
return None
|
||||
used = [s for s in job.get("slots") or [] if isinstance(s, dict)]
|
||||
info = self.print_info or {}
|
||||
pr, machine = info.get("print") or {}, info.get("machine") or {}
|
||||
progress = machine.get("progress")
|
||||
return {
|
||||
"file": str(job.get("file") or pr.get("filename") or "").rsplit("/", 1)[-1],
|
||||
"started": job.get("started"),
|
||||
"grams": round(sum(_num(s.get("grams")) for s in used), 2),
|
||||
"slots": [{"slot": i, "grams": round(_num(s.get("grams")), 2)} for i, s in enumerate(used)
|
||||
if _num(s.get("grams")) > 0],
|
||||
"progress": progress if isinstance(progress, (int, float)) else None,
|
||||
"layer": pr.get("current_layer"),
|
||||
"layers": pr.get("total_layers"),
|
||||
"remaining": pr.get("remaining"),
|
||||
"state": pr.get("state"),
|
||||
}
|
||||
|
||||
|
||||
class Bridge:
|
||||
def __init__(self, data_dir: Path, settings: SettingsStore, keys: KeyStore, *,
|
||||
|
||||
Reference in new issue
Block a user