From d863287f1f27f2af893257fb721d3fbec50589ac Mon Sep 17 00:00:00 2001 From: Jonas Menzel Date: Sun, 25 Oct 2026 22:53:12 +0100 Subject: [PATCH] CC2 Control copies take the colour the printer reports for the tray CC2 Control compares a spool's colour exactly with the tray's, which is the tag's colour from the display palette (e.g. #F72221 for Spoolman's #FF0000), and flags the slot or asks again when they differ. New copies take the tag's colour; copies in a slot are set to the tray's colour, others keep theirs. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014GcEk5tkfFSYD6NTF66NkC --- app/bridge.py | 23 ++++++++++++++++++----- tests/test_bridge.py | 31 ++++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/app/bridge.py b/app/bridge.py index 769616e..2b594c3 100644 --- a/app/bridge.py +++ b/app/bridge.py @@ -153,9 +153,13 @@ def cc2_spool_fields(sm: dict, channel: dict | None) -> tuple[dict, list[str]]: material = new if not material: material = "PLA" - color = color_hex(sm) + # CC2 Control compares the spool's colour exactly with what the printer reports for the tray, which is + # the tag's (a colour of the display palette): take that, so it does not flag the slot or ask again + color = tagcodec.normalize_hex_color(str(channel.get("color") or "").replace("0x", "")) if color is None: - color = tagcodec.normalize_hex_color(str(channel.get("color") or "").replace("0x", "")) or "000000" + color = color_hex(sm) + if color is None: + color = "000000" notes.append(f"Spoolman-Spule hat keine Farbe; verwende #{color}") fields: dict = { "name": label(sm), @@ -686,6 +690,12 @@ class PrinterWorker: mapped = {m["cc2"]: int(k) for k, m in self.st["slots"].items() if m.get("cc2")} in_tray = {s["spool_id"] for s in self.record_slots if s["spool_id"] is not None and self.tray_status.get(s["slot"]) in (1, 2)} + tray_color = {} # copy in a slot -> the colour the printer reports there + for s in cc2.get("slots") or []: + c = (s.get("printer") or {}).get("color") if isinstance(s.get("printer"), dict) else None + c = tagcodec.normalize_hex_color(str(c or "").replace("0x", "")) + if slot_spool_id(s) and c: + tray_color[slot_spool_id(s)] = c for sp in cc2.get("spools") or []: smid = spoolman_id_of(sp) if smid is None: @@ -711,14 +721,17 @@ class PrinterWorker: continue if sp.get("archived"): continue - self._sync_fields(cid, sp, sm, name) + self._sync_fields(cid, sp, sm, name, tray_color.get(cid)) if cid in mapped: self._sync_remaining(cid, sp, sm, name) except CC2Error as e: self._log("error", f"{name}: {e}") - def _sync_fields(self, cid: str, sp: dict, sm: dict, name: str) -> None: - fields, _ = cc2_spool_fields(sm, None) + def _sync_fields(self, cid: str, sp: dict, sm: dict, name: str, tray_color: str | None) -> None: + """Master data from Spoolman; the colour only for a copy in a slot, and then the printer's colour there.""" + fields, _ = cc2_spool_fields(sm, {"color": tray_color} if tray_color else None) + if not tray_color: + fields.pop("color", None) changed = {} for key, want in fields.items(): if key in ("remaining", "note", "material"): diff --git a/tests/test_bridge.py b/tests/test_bridge.py index eee9fc1..14b75cf 100644 --- a/tests/test_bridge.py +++ b/tests/test_bridge.py @@ -69,14 +69,14 @@ def poll(e, n=1, step=5): def test_scan_creates_assigns_and_sets_location(env): env["sm"].add(5, vendor="Sunlu", material="PLA+", color="1E40AF", remaining=812.5) - insert(env, 1, 5) + insert(env, 1, 5, color="0x2850E0") # the tag carries the nearest colour of the display palette poll(env) cc2 = env["cc2"] saves = [c for c in cc2.calls if c[0] == "save"] assert len(saves) == 1 f = saves[0][1] assert f["name"] == "SUNLU005" and f["brand"] == "Sunlu" and f["material"] == "PLA+" - assert f["color"] == "#1E40AF" and f["note"] == "spoolman:5" and f["remaining"] == 812.5 + assert f["color"] == "#2850E0" and f["note"] == "spoolman:5" and f["remaining"] == 812.5 # as the printer reports it assert f["net"] == 1000 and f["tare"] == 150 and f["diameter"] == 1.75 and f["density"] == 1.24 assert ("assign", 1, "s0001") in cc2.calls assert cc2.slots[1]["spool"] == "s0001" and cc2.slots[1]["question"] == "" @@ -356,7 +356,8 @@ def test_fields_use_tag_material_when_spoolman_material_does_not_fit(): sm = FakeSpoolman().add(3, material="Silk PLA", color="FFD700") fields, notes = cc2_spool_fields(sm, channel(10003, type_="PLA", detailed="PLA Silk")) assert fields["material"] == "PLA Silk" and notes - assert fields["color"] == "#FFD700" + assert fields["color"] == "#000000" # the tray's (tag's) colour, which CC2 Control compares with + assert cc2_spool_fields(sm, None)[0]["color"] == "#FFD700" # no tray known: Spoolman's # ---------- v2: several printers, Spoolman location and extra fields ---------- @@ -883,18 +884,34 @@ def test_rest_not_adjusted_before_use_is_booked(env): def test_master_data_follows_spoolman(env): env["sm"].add(5, color="4A4A4A") - insert(env, 0, 5) + insert(env, 0, 5, color="0x898989") poll(env, 2) - env["sm"].db[5]["filament"]["color_hex"] = "898989" + assert env["cc2"].library[0]["color"] == "#898989" # created with the tag's colour env["sm"].db[5]["filament"]["density"] = 1.3 + env["sm"].db[5]["lot_nr"] = "SUNLU005B" env["bridge"].spool_cache.clear() poll(env, 14) - assert ("save", {"id": "s0001", "color": "#898989", "density": 1.3}) in env["cc2"].calls - assert env["cc2"].library[0]["color"] == "#898989" + assert ("save", {"id": "s0001", "name": "SUNLU005B", "density": 1.3}) in env["cc2"].calls poll(env, 14) assert len([c for c in env["cc2"].calls if c[0] == "save" and "id" in c[1]]) == 1 +def test_copy_colour_follows_the_printers_report(env): + """A copy made with Spoolman's exact colour is flagged by CC2 Control; it gets the tray's colour.""" + env["sm"].add(4, color="FF0000") + env["cc2"].save_spool({"name": "SUNLU004", "brand": "SUNLU", "material": "PLA+", "color": "#FF0000", + "note": "spoolman:4", "net": 1000, "tare": 150, "density": 1.24, "diameter": 1.75}) + insert(env, 0, 4, color="0xF72221") + env["cc2"].slots[0]["printer"] = {"status": 1, "color": "#F72221", "type": "PLA"} + poll(env, 14) + assert env["cc2"].library[0]["color"] == "#F72221" + take_out_of_tray(env, 0) + env["sm"].db[4]["filament"]["color_hex"] = "00FF00" + env["bridge"].spool_cache.clear() + poll(env, 14) + assert env["cc2"].library[0]["color"] == "#F72221" # not in a tray: colour left alone + + def test_copies_of_archived_or_deleted_spools_are_removed_when_not_loaded(env): env["sm"].add(5) env["sm"].add(6)