Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ def _env_to_int(name: str, default: int) -> int:
DISABLE_MISMATCH_WARNING = _env_to_bool("DISABLE_MISMATCH_WARNING", False)
CLEAR_ASSIGNMENT_WHEN_EMPTY = _env_to_bool("CLEAR_ASSIGNMENT_WHEN_EMPTY", False)
COLOR_DISTANCE_TOLERANCE = _env_to_int("COLOR_DISTANCE_TOLERANCE", 40)

DOWNLOADED_FILES = {} # Store locations of currently downloaded temp files
37 changes: 12 additions & 25 deletions filament_usage_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
from pathlib import Path
from urllib.parse import urlparse

from config import EXTERNAL_SPOOL_AMS_ID, EXTERNAL_SPOOL_ID, TRACK_LAYER_USAGE
from config import EXTERNAL_SPOOL_AMS_ID, EXTERNAL_SPOOL_ID, TRACK_LAYER_USAGE, PRINTER_IP, PRINTER_NAME
from spoolman_client import consumeSpool
from spoolman_service import fetchSpools, getAMSFromTray, trayUid
from tools_3mf import download3mfFromCloud, download3mfFromFTP, download3mfFromLocalFilesystem
from tools_3mf import download3mfFromCloud, download3mfFromFTP, download3mfFromLocalFilesystem, clearTempFile, retrieveModel
from print_history import update_filament_spool, update_filament_grams_used, get_all_filament_usage_for_print, update_layer_tracking
from logger import log

Expand Down Expand Up @@ -299,7 +299,10 @@ def _handle_print_start(self, print_obj: dict) -> None:
log("[filament-tracker] Print start")

model_url = print_obj.get("url")
model_path = self._retrieve_model(model_url)
if not model_url:
log("[filament-tracker] No model URL provided")
return None
model_path = retrieveModel(model_url)

if model_path is None:
log("Failed to retrieve model. Print will not be tracked.")
Expand Down Expand Up @@ -419,28 +422,6 @@ def apply_ams_mapping(self, ams_mapping: list[int] | None) -> None:
self._maybe_update_predicted_total()
self._update_layer_tracking_progress()

def _retrieve_model(self, model_url: str | None) -> str | None:
if not model_url:
log("[filament-tracker] No model URL provided")
return None

uri = urlparse(model_url)
try:
with tempfile.NamedTemporaryFile(suffix=".3mf", delete=False) as model_file:
if uri.scheme in ("https", "http"):
log(f"[filament-tracker] Downloading model via HTTP(S): {model_url}")
download3mfFromCloud(model_url, model_file)
elif uri.scheme == "local":
log(f"[filament-tracker] Loading model from local path: {uri.path}")
download3mfFromLocalFilesystem(uri.path, model_file)
else:
log(f"[filament-tracker] Downloading model via FTP: {model_url}")
download3mfFromFTP(model_url.rpartition('/')[-1], model_file) # Pull just filename to clear out any unexpected paths
return model_file.name
except Exception as exc:
log(f"Failed to fetch model: {exc}")
return None

def _handle_layer_change(self, layer: int) -> None:
if self.active_model is None:
return
Expand Down Expand Up @@ -487,6 +468,9 @@ def _handle_print_end(self) -> None:
self._reset_layer_tracking_state()
clear_checkpoint()

log("[DEBUG] Clearing temp print file")
clearTempFile(PRINTER_NAME, PRINTER_IP)

def _handle_print_abort(self, status: str = LAYER_TRACKING_STATUS_ABORTED) -> None:
if self.active_model is None:
return
Expand All @@ -512,6 +496,9 @@ def _handle_print_abort(self, status: str = LAYER_TRACKING_STATUS_ABORTED) -> No
self._reset_layer_tracking_state()
clear_checkpoint()

log("[DEBUG] Clearing temp print file")
clearTempFile(PRINTER_NAME, PRINTER_IP)

def _mm_to_grams(self, length_mm: float, diameter_mm: float, density_g_per_cm3: float) -> float:
"""
Convert filament length in mm to grams.
Expand Down
100 changes: 57 additions & 43 deletions mqtt_bambulab.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
EXTERNAL_SPOOL_ID,
TRACK_LAYER_USAGE,
CLEAR_ASSIGNMENT_WHEN_EMPTY,
DOWNLOADED_FILES,
PRINTER_NAME,

)
from messages import GET_VERSION, PUSH_ALL, AMS_FILAMENT_SETTING
from spoolman_service import spendFilaments, setActiveTray, fetchSpools, clear_active_spool_for_tray
from tools_3mf import getMetaDataFrom3mf
from tools_3mf import getMetaDataFrom3mf, clearTempFile
import time
import copy
from collections.abc import Mapping
Expand Down Expand Up @@ -158,44 +161,44 @@ def update_dict(original: dict, updates: dict) -> dict:
return original


def _parse_grams(value):
try:
return float(value)
except (TypeError, ValueError):
return None

def _mask_serial(serial: str | None, keep_chars: int = 3) -> str:
if not serial:
return ""
visible = serial[:keep_chars]
if len(serial) <= keep_chars:
return visible
return f"{visible}..."

def _mask_sn_values(value):
if isinstance(value, dict):
for key, item in value.items():
if key.lower() == "sn" and isinstance(item, str):
value[key] = _mask_serial(item)
else:
_mask_sn_values(item)
elif isinstance(value, list):
for elem in value:
_mask_sn_values(elem)

def _mask_mqtt_payload(payload: str) -> str:
try:
data = json.loads(payload)
_mask_sn_values(data)
masked = json.dumps(data, separators=(",", ":"))
except ValueError:
masked = payload

masked_serial = _mask_serial(PRINTER_ID)
if masked_serial:
masked = masked.replace(PRINTER_ID, masked_serial)

return masked
def _parse_grams(value):
try:
return float(value)
except (TypeError, ValueError):
return None
def _mask_serial(serial: str | None, keep_chars: int = 3) -> str:
if not serial:
return ""
visible = serial[:keep_chars]
if len(serial) <= keep_chars:
return visible
return f"{visible}..."
def _mask_sn_values(value):
if isinstance(value, dict):
for key, item in value.items():
if key.lower() == "sn" and isinstance(item, str):
value[key] = _mask_serial(item)
else:
_mask_sn_values(item)
elif isinstance(value, list):
for elem in value:
_mask_sn_values(elem)
def _mask_mqtt_payload(payload: str) -> str:
try:
data = json.loads(payload)
_mask_sn_values(data)
masked = json.dumps(data, separators=(",", ":"))
except ValueError:
masked = payload
masked_serial = _mask_serial(PRINTER_ID)
if masked_serial:
masked = masked.replace(PRINTER_ID, masked_serial)
return masked

def map_filament(tray_tar):
global PENDING_PRINT_METADATA
Expand Down Expand Up @@ -292,7 +295,14 @@ def processMessage(data):
length_used=length_used,
estimated_length=estimated_length_mm,
)


# # Clear out temp files if they exist on FAILED or FINISHED prints
# if(DOWNLOADED_FILES.get(f"{PRINTER_NAME}_{PRINTER_IP}") and PRINTER_STATE_LAST["print"].get("gecode_state") == "RUNNING" and (PRINTER_STATE["print"].get("gcode_state") == "FAILED" or PRINTER_STATE["print"].get("gcode_state") == "FINISHED")):
# log("would of cleared here")
# #clearTempFile(PRINTER_NAME, PRINTER_IP)



#if ("gcode_state" in data["print"] and data["print"]["gcode_state"] == "RUNNING") and ("print_type" in data["print"] and data["print"]["print_type"] != "local") \
# and ("tray_tar" in data["print"] and data["print"]["tray_tar"] != "255") and ("stg_cur" in data["print"] and data["print"]["stg_cur"] == 0 and PRINT_CURRENT_STAGE != 0):

Expand All @@ -306,7 +316,11 @@ def processMessage(data):
):

if not PENDING_PRINT_METADATA:
PENDING_PRINT_METADATA = getMetaDataFrom3mf(PRINTER_STATE["print"]["gcode_file"])
# TODO Seems to be trying to find an internal gcode file inside the 3mf file?
# And only firing sometimes, do we need this?
#PENDING_PRINT_METADATA = getMetaDataFrom3mf(PRINTER_STATE["print"]["gcode_file"])
log(f"[DEBUG] Would have tried to grab file {PRINTER_STATE["print"]["gcode_file"]}")

if PENDING_PRINT_METADATA:
PENDING_PRINT_METADATA["print_type"] = PRINTER_STATE["print"].get("print_type")
PENDING_PRINT_METADATA["task_id"] = PRINTER_STATE["print"].get("task_id")
Expand Down Expand Up @@ -449,8 +463,8 @@ def on_message(client, userdata, msg):
"models_by_id": models_by_id,
}

if "print" in data:
append_to_rotating_file("/home/app/logs/mqtt.log", _mask_mqtt_payload(msg.payload.decode()))
if "print" in data:
append_to_rotating_file("/home/app/logs/mqtt.log", _mask_mqtt_payload(msg.payload.decode()))

#print(data)

Expand Down
Loading
Loading