"""Elegoo Canvas material and subtype codes. Taken verbatim from cjtallman/canvas-rfid-tool (index.html, MIT License, Copyright (c) 2026 cjtallman, see THIRD_PARTY_NOTICES.md), which in turn credits DnG-Crafts/ELG-RFID and Savion/elegoo-rfid-editor. """ from __future__ import annotations import re # name -> (4-byte material code, subtype family byte, suggested nozzle min/max) MATERIALS: dict[str, dict] = { "PLA": {"code": 0x00807665, "family": 0x00, "temps": (190, 230)}, "PETG": {"code": 0x80698471, "family": 0x01, "temps": (230, 260)}, "ABS": {"code": 0x00656683, "family": 0x02, "temps": (240, 270)}, "TPU": {"code": 0x00848085, "family": 0x03, "temps": (210, 240)}, "PA": {"code": 0x00008065, "family": 0x04, "temps": (260, 290)}, "CPE": {"code": 0x00678069, "family": 0x05, "temps": (240, 270)}, "PC": {"code": 0x00008067, "family": 0x06, "temps": (260, 290)}, "PVA": {"code": 0x00808665, "family": 0x07, "temps": (190, 220)}, "ASA": {"code": 0x00658365, "family": 0x08, "temps": (240, 270)}, "BVOH": {"code": 0x42564F48, "family": 0x09, "temps": (190, 220)}, "EVA": {"code": 0x00455641, "family": 0x0A, "temps": (190, 220)}, "HIPS": {"code": 0x48495053, "family": 0x0B, "temps": (230, 250)}, "PP": {"code": 0x00005050, "family": 0x0C, "temps": (220, 250)}, "PPA": {"code": 0x00505041, "family": 0x0D, "temps": (280, 310)}, "PPS": {"code": 0x00505053, "family": 0x0E, "temps": (300, 330)}, } # (code, name, hidden on the CC2 display) SUBTYPES: list[tuple[int, str, bool]] = [ (0x0000, "PLA", False), (0x0001, "PLA+", False), (0x0002, "PLA Pro", False), (0x0003, "PLA Silk", False), (0x0004, "PLA-CF", False), (0x0005, "PLA Carbon", True), (0x0006, "PLA Matte", False), (0x0007, "PLA Fluo", False), (0x0008, "PLA Wood", False), (0x0009, "PLA Basic", False), (0x000A, "RAPID PLA+", False), (0x000B, "PLA Marble", False), (0x000C, "PLA Galaxy", False), (0x000D, "PLA Red Copper", False), (0x000E, "PLA Sparkle", True), (0x0100, "PETG", False), (0x0101, "PETG-CF", False), (0x0102, "PETG-GF", False), (0x0103, "PETG Pro", True), (0x0104, "PETG Translucent", False), (0x0105, "RAPID PETG", False), (0x0200, "ABS", False), (0x0201, "ABS-GF", True), (0x0300, "TPU", False), (0x0301, "TPU 95A", False), (0x0302, "RAPID TPU 95A", False), (0x0400, "PA", False), (0x0401, "PA-CF", True), (0x0403, "PAHT-CF", False), (0x0404, "PA6", True), (0x0405, "PA6-CF", True), (0x0406, "PA12", True), (0x0407, "PA12-CF", True), (0x0500, "CPE", False), (0x0600, "PC", False), (0x0601, "PCTG", True), (0x0602, "PC-FR", False), (0x0700, "PVA", False), (0x0800, "ASA", False), (0x0900, "BVOH", False), (0x0A00, "EVA", False), (0x0B00, "HIPS", False), (0x0C00, "PP", False), (0x0C01, "PP-CF", True), (0x0C02, "PP-GF", True), (0x0D00, "PPA", False), (0x0D01, "PPA-CF", True), (0x0D02, "PPA-GF", True), (0x0E00, "PPS", False), (0x0E02, "PPS-CF", True), ] SUBTYPE_BY_CODE = {code: name for code, name, _ in SUBTYPES} MATERIAL_BY_CODE = {m["code"]: name for name, m in MATERIALS.items()} def material_of_subtype(code: int) -> str | None: family = code >> 8 for name, m in MATERIALS.items(): if m["family"] == family: return name return None def subtypes_for(material: str) -> list[tuple[int, str, bool]]: family = MATERIALS[material]["family"] return [s for s in SUBTYPES if s[0] >> 8 == family] def _tokens(text: str) -> set[str]: text = text.upper().replace("+", " PLUS ") return set(re.findall(r"[A-Z0-9]+", text)) # Spoolman material spellings that do not name the Elegoo base material directly. _ALIASES = {"PCTG": "PC", "PA6": "PA", "PA12": "PA", "PAHT": "PA", "NYLON": "PA", "PET": "PETG"} _MODIFIERS = [ # (tokens that must all be present, subtype name suffix) ({"RAPID", "PLUS"}, "RAPID {m}+"), ({"RAPID"}, "RAPID {m}"), ({"95A"}, "{m} 95A"), ({"SILK"}, "{m} Silk"), ({"MATTE"}, "{m} Matte"), ({"MATT"}, "{m} Matte"), ({"CF"}, "{m}-CF"), ({"CARBON"}, "{m}-CF"), ({"GF"}, "{m}-GF"), ({"WOOD"}, "{m} Wood"), ({"MARBLE"}, "{m} Marble"), ({"GALAXY"}, "{m} Galaxy"), ({"SPARKLE"}, "{m} Sparkle"), ({"FLUO"}, "{m} Fluo"), ({"TRANSLUCENT"}, "{m} Translucent"), ({"TRANSPARENT"}, "{m} Translucent"), ({"PRO"}, "{m} Pro"), ({"BASIC"}, "{m} Basic"), ({"PLUS"}, "{m}+"), ] def suggest(material: str | None, name: str | None = "") -> dict: """Suggest Elegoo material, subtype and nozzle temps from Spoolman text.""" material = material or "" tokens = _tokens(f"{material} {name or ''}") mat_tokens = _tokens(material) base = None # Prefer the Spoolman material field; fall back to the filament name. for pool in (mat_tokens, tokens): for tok in sorted(pool, key=len, reverse=True): cand = _ALIASES.get(tok, tok) if cand in MATERIALS: base = cand break if tok.startswith("PLA"): # e.g. "PLAPLUS" base = "PLA" break if base: break if base is None: base = "PLA" subtype = MATERIALS[base]["family"] << 8 names = {n.upper(): c for c, n, _ in subtypes_for(base)} # Exact subtype names first (e.g. "PCTG", "PAHT-CF"). for tok in tokens: if tok in names and tok != base: subtype = names[tok] break else: for needed, pattern in _MODIFIERS: if needed <= tokens: cand = pattern.format(m=base).upper() if cand in names: subtype = names[cand] break return {"material": base, "subtype": subtype, "temps": MATERIALS[base]["temps"]}