Grotti 2.5.1: Druckbericht, Statistik, Spulenakte und Protokoll-Fixes.
This commit is contained in:
1 parent
b277ca48aa
commit
efd0801cd1
30 files changed
+3393
-178
No files matched your search
+159
-1
@@ -418,6 +418,64 @@ def steuern(host, action, params=None):
|
||||
if not re.fullmatch(r"[A-Za-z0-9._+\-]+", name):
|
||||
raise PrinterError("Ungültiger Objektname.")
|
||||
return gcode_script(host, "EXCLUDE_OBJECT NAME=%s" % name, timeout=20)
|
||||
if action == "dryer_start":
|
||||
try:
|
||||
unit = int(params.get("id"))
|
||||
except (TypeError, ValueError) as exc:
|
||||
raise PrinterError("ACE-Box muss 0 oder 1 sein.") from exc
|
||||
if unit not in (0, 1):
|
||||
raise PrinterError("ACE-Box muss 0 oder 1 sein.")
|
||||
try:
|
||||
temp = int(round(float(params.get("temp"))))
|
||||
duration = int(round(float(params.get("duration"))))
|
||||
except (TypeError, ValueError) as exc:
|
||||
raise PrinterError("Temperatur und Dauer müssen Zahlen sein.") from exc
|
||||
if temp < 35 or temp > 65:
|
||||
raise PrinterError("Trockner-Temperatur außerhalb 35–65 °C.")
|
||||
if duration < 1 or duration > 720:
|
||||
raise PrinterError("Trockner-Dauer außerhalb 1–720 Minuten.")
|
||||
return post_json(
|
||||
host,
|
||||
"/server/filament_hub/start_drying",
|
||||
{"id": unit, "temp": temp, "duration": duration, "fan_speed": 0},
|
||||
timeout=15,
|
||||
)
|
||||
if action == "dryer_stop":
|
||||
try:
|
||||
unit = int(params.get("id"))
|
||||
except (TypeError, ValueError) as exc:
|
||||
raise PrinterError("ACE-Box muss 0 oder 1 sein.") from exc
|
||||
if unit not in (0, 1):
|
||||
raise PrinterError("ACE-Box muss 0 oder 1 sein.")
|
||||
return post_json(host, "/server/filament_hub/stop_drying", {"id": unit}, timeout=15)
|
||||
if action == "slot_setzen":
|
||||
hub_id = _int_or_none(params.get("id"))
|
||||
index = _int_or_none(params.get("index"))
|
||||
if hub_id not in (0, 1) or index is None or index < 0 or index > 3:
|
||||
raise PrinterError("ACE-Platz ungültig.")
|
||||
material = str(params.get("type") or "").strip()
|
||||
if not material:
|
||||
raise PrinterError("Die Spule braucht eine Sorte.")
|
||||
color = params.get("color")
|
||||
if not isinstance(color, dict):
|
||||
raise PrinterError("Die Spule braucht eine Farbe.")
|
||||
try:
|
||||
red = max(0, min(255, int(color.get("R"))))
|
||||
green = max(0, min(255, int(color.get("G"))))
|
||||
blue = max(0, min(255, int(color.get("B"))))
|
||||
except (TypeError, ValueError) as exc:
|
||||
raise PrinterError("Die Spule braucht eine Farbe.") from exc
|
||||
return post_json(
|
||||
host,
|
||||
"/printer/filament_hub/set_filament_info",
|
||||
{
|
||||
"id": hub_id,
|
||||
"index": index,
|
||||
"type": material,
|
||||
"color": {"R": red, "G": green, "B": blue},
|
||||
},
|
||||
timeout=15,
|
||||
)
|
||||
if action == "firmware_restart":
|
||||
# GoKlipper/Rinkhals: FIRMWARE_RESTART deadlocked gklib (Rinkhals #32).
|
||||
raise PrinterError(
|
||||
@@ -600,6 +658,80 @@ def extract_slots(status):
|
||||
return found[:8]
|
||||
|
||||
|
||||
def _int_or_none(value):
|
||||
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
||||
return None
|
||||
return int(value)
|
||||
|
||||
|
||||
def extract_dryers(status):
|
||||
"""Echte ACE-Boxen aus mmu_machine (num_gates > 0), Dummy-Unit bleibt weg."""
|
||||
machine = (status or {}).get("mmu_machine")
|
||||
if not isinstance(machine, dict):
|
||||
return []
|
||||
found = []
|
||||
for unit_id in (0, 1):
|
||||
unit = machine.get("unit_%d" % unit_id)
|
||||
if not isinstance(unit, dict):
|
||||
continue
|
||||
gates = _int_or_none(unit.get("num_gates"))
|
||||
if gates is None or gates < 1:
|
||||
continue
|
||||
found.append({
|
||||
"id": unit_id,
|
||||
"status": str(unit.get("dryer_status") or "stop"),
|
||||
"temp": _int_or_none(unit.get("dryer_temp")) or 0,
|
||||
"target": _int_or_none(unit.get("dryer_target_temp")) or 0,
|
||||
"remain": _int_or_none(unit.get("dryer_remaining")) or 0,
|
||||
"humidity": _int_or_none(unit.get("dryer_humidity")) or 0,
|
||||
"gates": gates,
|
||||
})
|
||||
return found
|
||||
|
||||
|
||||
def ace_hub_index(ace_slot):
|
||||
"""Grotti-Platz 1–8 → Moonraker id (0/1) und index in der Box (0–3)."""
|
||||
try:
|
||||
slot = int(ace_slot)
|
||||
except (TypeError, ValueError) as exc:
|
||||
raise PrinterError("ACE-Platz muss 1–8 sein.") from exc
|
||||
if slot < 1 or slot > 8:
|
||||
raise PrinterError("ACE-Platz muss 1–8 sein.")
|
||||
return (slot - 1) // 4, (slot - 1) % 4
|
||||
|
||||
|
||||
def spool_rgb(spool):
|
||||
"""Erste Farbe der Spule als RGB-Dict für set_filament_info."""
|
||||
colors = (spool or {}).get("colors") or []
|
||||
if isinstance(colors, str):
|
||||
try:
|
||||
colors = json.loads(colors)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
colors = []
|
||||
hex_value = ""
|
||||
if isinstance(colors, list):
|
||||
for item in colors:
|
||||
if isinstance(item, dict) and item.get("hex"):
|
||||
hex_value = str(item["hex"]).strip()
|
||||
break
|
||||
if isinstance(item, str) and item.strip():
|
||||
hex_value = item.strip()
|
||||
break
|
||||
if not hex_value:
|
||||
hex_value = str((spool or {}).get("color") or "").strip()
|
||||
if hex_value.startswith("#"):
|
||||
hex_value = hex_value[1:]
|
||||
if len(hex_value) == 8:
|
||||
hex_value = hex_value[:6]
|
||||
if len(hex_value) != 6 or any(ch not in "0123456789abcdefABCDEF" for ch in hex_value):
|
||||
return None
|
||||
return {
|
||||
"R": int(hex_value[0:2], 16),
|
||||
"G": int(hex_value[2:4], 16),
|
||||
"B": int(hex_value[4:6], 16),
|
||||
}
|
||||
|
||||
|
||||
def slots(host, status=None):
|
||||
if status is not None:
|
||||
found = extract_slots(status)
|
||||
@@ -868,6 +1000,8 @@ def query(host):
|
||||
extras = []
|
||||
if "probe" in names:
|
||||
extras.append("probe")
|
||||
if "mmu_machine" in names:
|
||||
extras.append("mmu_machine")
|
||||
url = "http://%s/printer/objects/query?%s" % (host, "&".join([base] + extras))
|
||||
payload = fetch_json(url)
|
||||
status = payload["result"]["status"]
|
||||
@@ -875,6 +1009,21 @@ def query(host):
|
||||
card = status.get("virtual_sdcard") or {}
|
||||
extruder = status.get("extruder") or {}
|
||||
bed = status.get("heater_bed") or {}
|
||||
mmu = status.get("mmu") or {}
|
||||
mmu_tool = mmu.get("tool")
|
||||
if not isinstance(mmu_tool, (int, float)) or isinstance(mmu_tool, bool):
|
||||
mmu_tool = mmu.get("gate")
|
||||
if isinstance(mmu_tool, (int, float)) and not isinstance(mmu_tool, bool):
|
||||
mmu_tool = int(mmu_tool)
|
||||
if mmu_tool < 0:
|
||||
mmu_tool = None
|
||||
else:
|
||||
mmu_tool = None
|
||||
filament_used = stats.get("filament_used")
|
||||
if not isinstance(filament_used, (int, float)) or isinstance(filament_used, bool):
|
||||
filament_used = None
|
||||
else:
|
||||
filament_used = float(filament_used)
|
||||
raw_name = stats.get("filename") or ""
|
||||
filename = raw_name.replace("\\", "/").rsplit("/", 1)[-1] if raw_name else ""
|
||||
meta = metadata(host, raw_name) if raw_name else {}
|
||||
@@ -933,8 +1082,15 @@ def query(host):
|
||||
"bed_target": bed.get("target"),
|
||||
"chamber": chamber,
|
||||
"chamber_target": chamber_target,
|
||||
"filament_used": filament_used,
|
||||
"filament_total": meta.get("filament_total") or 0,
|
||||
"mmu_tool": mmu_tool,
|
||||
"material": card.get("filament_type") or meta.get("filament_type") or "",
|
||||
"weight": meta.get("filament_weight_total") or 0,
|
||||
"weight": (
|
||||
sum(float(x or 0) for x in meta["filament_weight_total"])
|
||||
if isinstance(meta.get("filament_weight_total"), list)
|
||||
else (meta.get("filament_weight_total") or 0)
|
||||
),
|
||||
"height": meta.get("object_height") or 0,
|
||||
"layer_height": meta.get("layer_height") or card.get("layer_height") or 0,
|
||||
"estimated": meta.get("estimated_time") or 0,
|
||||
@@ -952,6 +1108,7 @@ def query(host):
|
||||
"speed_factor": speed_factor,
|
||||
"extrude_factor": extrude_factor,
|
||||
"slots": slots(host, status),
|
||||
"dryers": extract_dryers(status),
|
||||
"exclude": exclude_status(host),
|
||||
"anycubic_task": _anycubic_task_field(host),
|
||||
}
|
||||
@@ -993,6 +1150,7 @@ def snapshot(printers):
|
||||
"speed_factor": None,
|
||||
"extrude_factor": None,
|
||||
"slots": [],
|
||||
"dryers": [],
|
||||
"anycubic_task": None,
|
||||
})
|
||||
card.update(mesh_fields(printer["host"]))
|
||||
|
||||
Reference in new issue
Block a user