openspoolman/app.py
Filip Bednárik 96a85271c0 Bugfix: External spool
Improvement: Use fragments in templates
Features: Manual fill of empty tray
Bump version to 0.1.3
2024-12-22 23:32:58 +01:00

194 lines
7.1 KiB
Python

import json
import traceback
import uuid
from flask import Flask, request, render_template, redirect, url_for
from config import BASE_URL, AUTO_SPEND, SPOOLMAN_BASE_URL
from filament import generate_filament_brand_code, generate_filament_temperatures
from frontend_utils import color_is_dark
from messages import AMS_FILAMENT_SETTING
from mqtt_bambulab import fetchSpools, getLastAMSConfig, publish, getMqttClient, setActiveTray
from spoolman_client import patchExtraTags, getSpoolById
from spoolman_service import augmentTrayDataWithSpoolMan, trayUid
app = Flask(__name__)
EXTERNAL_SPOOL_AMS_ID = 255
@app.context_processor
def fronted_utilities():
return dict(SPOOLMAN_BASE_URL=SPOOLMAN_BASE_URL, AUTO_SPEND=AUTO_SPEND, color_is_dark=color_is_dark, BASE_URL=BASE_URL, EXTERNAL_SPOOL_AMS_ID=EXTERNAL_SPOOL_AMS_ID)
@app.route("/issue")
def issue():
return render_template('issue.html')
@app.route("/fill")
def fill():
ams_id = request.args.get("ams")
tray_id = request.args.get("tray")
if not all([ams_id, tray_id]):
return "Missing AMS ID, or Tray ID."
spool_id = request.args.get("spool_id")
if spool_id:
spool_data = getSpoolById(spool_id)
setActiveTray(spool_id, spool_data["extra"], ams_id, tray_id)
setActiveSpool(ams_id, tray_id, spool_data)
return redirect(url_for('home', success_message=f"Updated Spool ID {spool_id} to AMS {ams_id}, Tray {tray_id}."))
else:
spools = fetchSpools()
return render_template('fill.html', spools=spools, ams_id=ams_id, tray_id=tray_id)
@app.route("/spool_info")
def spool_info():
try:
tag_id = request.args.get("tag_id")
last_ams_config = getLastAMSConfig()
ams_data = last_ams_config.get("ams", [])
vt_tray_data = last_ams_config.get("vt_tray", {})
print(ams_data)
print(vt_tray_data)
if not tag_id:
return "TAG ID is required as a query parameter (e.g., ?tagid=RFID123)"
spools = fetchSpools()
current_spool = None
for spool in spools:
if not spool.get("extra", {}).get("tag"):
continue
tag = json.loads(spool["extra"]["tag"])
if tag != tag_id:
continue
current_spool = spool
# TODO: missing current_spool
return render_template('spool_info.html', tag_id=tag_id, current_spool=current_spool, ams_data=ams_data, vt_tray_data=vt_tray_data)
except Exception as e:
traceback.print_exc()
return render_template('error.html', exception=str(e))
@app.route("/tray_load")
def tray_load():
tag_id = request.args.get("tag_id")
ams_id = request.args.get("ams")
tray_id = request.args.get("tray")
spool_id = request.args.get("spool_id")
if not all([tag_id, ams_id, tray_id, spool_id]):
return "Missing RFID, AMS ID, or Tray ID or spool_id."
try:
# Update Spoolman with the selected tray
spool_data = getSpoolById(spool_id)
setActiveTray(spool_id, spool_data["extra"], ams_id, tray_id)
setActiveSpool(ams_id, tray_id, spool_data)
return redirect(url_for('home', success_message=f"Updated Spool ID {spool_id} with TAG id {tag_id} to AMS {ams_id}, Tray {tray_id}."))
except Exception as e:
traceback.print_exc()
return render_template('error.html', exception=str(e))
def setActiveSpool(ams_id, tray_id, spool_data):
ams_message = AMS_FILAMENT_SETTING
ams_message["print"]["sequence_id"] = 0
ams_message["print"]["ams_id"] = int(ams_id)
ams_message["print"]["tray_id"] = int(tray_id)
ams_message["print"]["tray_color"] = spool_data["filament"]["color_hex"].upper() + "FF"
if "nozzle_temperature" in spool_data["filament"]["extra"]:
nozzle_temperature_range = spool_data["filament"]["extra"]["nozzle_temperature"].strip("[]").split(",")
ams_message["print"]["nozzle_temp_min"] = int(nozzle_temperature_range[0])
ams_message["print"]["nozzle_temp_max"] = int(nozzle_temperature_range[1])
else:
nozzle_temperature_range_obj = generate_filament_temperatures(spool_data["filament"]["material"],
spool_data["filament"]["vendor"]["name"])
ams_message["print"]["nozzle_temp_min"] = int(nozzle_temperature_range_obj["filament_min_temp"])
ams_message["print"]["nozzle_temp_max"] = int(nozzle_temperature_range_obj["filament_max_temp"])
ams_message["print"]["tray_type"] = spool_data["filament"]["material"]
filament_brand_code = generate_filament_brand_code(spool_data["filament"]["material"],
spool_data["filament"]["vendor"]["name"],
spool_data["filament"]["extra"].get("type", ""))
ams_message["print"]["tray_info_idx"] = filament_brand_code["brand_code"]
# TODO: test sub_brand_code
# ams_message["print"]["tray_sub_brands"] = filament_brand_code["sub_brand_code"]
ams_message["print"]["tray_sub_brands"] = ""
print(ams_message)
publish(getMqttClient(), ams_message)
@app.route("/")
def home():
try:
last_ams_config = getLastAMSConfig()
ams_data = last_ams_config.get("ams", [])
vt_tray_data = last_ams_config.get("vt_tray", {})
spool_list = fetchSpools()
success_message = request.args.get("success_message")
issue = False
#TODO: Fix issue when external spool info is reset via bambulab interface
augmentTrayDataWithSpoolMan(spool_list, vt_tray_data, trayUid(EXTERNAL_SPOOL_AMS_ID, vt_tray_data["id"]))
issue |= vt_tray_data["issue"]
for ams in ams_data:
for tray in ams["tray"]:
augmentTrayDataWithSpoolMan(spool_list, tray, trayUid(ams["id"], tray["id"]))
issue |= tray["issue"]
return render_template('index.html', success_message=success_message, ams_data=ams_data, vt_tray_data=vt_tray_data, issue=issue)
except Exception as e:
traceback.print_exc()
return render_template('error.html', exception=str(e))
def sort_spools(spools):
def condition(item):
# Ensure the item has an "extra" key and is a dictionary
if not isinstance(item, dict) or "extra" not in item or not isinstance(item["extra"], dict):
return False
# Check the specified condition
return item["extra"].get("tag") or item["extra"].get("tag") == ""
# Sort with the custom condition: False values come first
return sorted(spools, key=lambda spool: bool(condition(spool)))
@app.route("/assign_tag")
def assign_tag():
try:
spools = sort_spools(fetchSpools())
return render_template('assign_tag.html', spools=spools)
except Exception as e:
traceback.print_exc()
return render_template('error.html', exception=str(e))
@app.route("/write_tag")
def write_tag():
try:
spool_id = request.args.get("spool_id")
if not spool_id:
return "spool ID is required as a query parameter (e.g., ?spool_id=1)"
myuuid = str(uuid.uuid4())
patchExtraTags(spool_id, {}, {
"tag": json.dumps(myuuid),
})
return render_template('write_tag.html', myuuid=myuuid)
except Exception as e:
traceback.print_exc()
return render_template('error.html', exception=str(e))
@app.route('/', methods=['GET'])
def health():
return "OK", 200