From e87c943010230bb157762bad7a062b0406ebacd4 Mon Sep 17 00:00:00 2001 From: jQu Date: Thu, 11 Dec 2025 20:49:04 +0100 Subject: [PATCH 1/2] Adding QR code generation --- app.py | 105 ++++++++++++++++++++++++--- requirements.txt | 1 + templates/assign_tag.html | 2 +- templates/base.html | 2 +- templates/fragments/list_spools.html | 9 ++- templates/write_tag.html | 60 +++++++++++---- 6 files changed, 149 insertions(+), 30 deletions(-) diff --git a/app.py b/app.py index a7b846fb..2fc38e25 100644 --- a/app.py +++ b/app.py @@ -1,10 +1,13 @@ +import io import json import math import os import traceback import uuid -from flask import Flask, request, render_template, redirect, url_for +import qrcode +from PIL import Image +from flask import Flask, request, render_template, redirect, url_for, send_file from config import BASE_URL, AUTO_SPEND, SPOOLMAN_BASE_URL, EXTERNAL_SPOOL_AMS_ID, EXTERNAL_SPOOL_ID, PRINTER_NAME from filament import generate_filament_brand_code, generate_filament_temperatures @@ -128,18 +131,18 @@ def fill(): return render_template('fill.html', spools=spools, ams_id=ams_id, tray_id=tray_id, materials=materials, selected_materials=selected_materials) -@app.route("/spool_info") -def spool_info(): - if not mqtt_bambulab.isMqttClientConnected(): +@app.route("/spool_info") +def spool_info(): + if not mqtt_bambulab.isMqttClientConnected(): return render_template('error.html', exception="MQTT is disconnected. Is the printer online?") try: tag_id = request.args.get("tag_id") spool_id = request.args.get("spool_id") - last_ams_config = mqtt_bambulab.getLastAMSConfig() + last_ams_config = mqtt_bambulab.getLastAMSConfig() ams_data = last_ams_config.get("ams", []) vt_tray_data = last_ams_config.get("vt_tray", {}) - spool_list = mqtt_bambulab.fetchSpools() + spool_list = mqtt_bambulab.fetchSpools() issue = False #TODO: Fix issue when external spool info is reset via bambulab interface @@ -154,7 +157,7 @@ def spool_info(): if not tag_id and not spool_id: return render_template('error.html', exception="TAG ID or spool_id is required as a query parameter (e.g., ?tag_id=RFID123 or ?spool_id=1)") - spools = mqtt_bambulab.fetchSpools() + spools = mqtt_bambulab.fetchSpools() current_spool = None spool_id_int = None @@ -207,9 +210,9 @@ def spoolman_compatible_spool_info(spool_id): return redirect(url_for('spool_info', **query_params)) -@app.route("/tray_load") -def tray_load(): - if not mqtt_bambulab.isMqttClientConnected(): +@app.route("/tray_load") +def tray_load(): + if not mqtt_bambulab.isMqttClientConnected(): return render_template('error.html', exception="MQTT is disconnected. Is the printer online?") tag_id = request.args.get("tag_id") @@ -375,7 +378,85 @@ def write_tag(): spoolman_client.patchExtraTags(spool_id, {}, { "tag": json.dumps(myuuid), }) - return render_template('write_tag.html', myuuid=myuuid) + return render_template('write_tag.html', myuuid=myuuid, spool_id=spool_id) + except Exception as e: + traceback.print_exc() + return render_template('error.html', exception=str(e)) + +@app.route("/qr_code") +def qr_code(): + try: + spool_id = request.args.get("spool_id") + tag_id = request.args.get("tag_id") + download = request.args.get("download", "false").lower() == "true" + + if not spool_id and not tag_id: + return render_template('error.html', exception="spool_id or tag_id is required as a query parameter") + + spool_data = None + # If tag_id is provided, use it; otherwise get it from spool + if not tag_id and spool_id: + spool_data = spoolman_client.getSpoolById(spool_id) + tag_value = spool_data.get("extra", {}).get("tag") + if tag_value: + tag_id = json.loads(tag_value) + else: + # If no tag exists, generate a new one + tag_id = str(uuid.uuid4()) + existing_extra = spool_data.get("extra", {}) + spoolman_client.patchExtraTags(spool_id, existing_extra, { + "tag": json.dumps(tag_id), + }) + elif tag_id and spool_id: + # If both are provided, get spool data for display + try: + spool_data = spoolman_client.getSpoolById(spool_id) + except Exception: + pass + elif tag_id and not spool_id: + # Try to find spool by tag_id from SpoolMan + try: + spools = spoolman_client.fetchSpoolList() + for spool in spools: + tag_value = spool.get("extra", {}).get("tag") + if tag_value: + stored_tag = json.loads(tag_value) + if stored_tag == tag_id: + spool_data = spool + spool_id = spool.get('id') + break + except Exception: + pass + + # Generate QR code URL + qr_url = f"{BASE_URL}/spool_info?tag_id={tag_id}" + + # Create QR code + qr = qrcode.QRCode( + version=1, + error_correction=qrcode.constants.ERROR_CORRECT_L, + box_size=6, + border=2, + ) + qr.add_data(qr_url) + qr.make(fit=True) + + # Create QR code image + qr_img = qr.make_image(fill_color="black", back_color="white") + # Convert to RGB mode to ensure compatibility + if qr_img.mode != 'RGB': + qr_img = qr_img.convert('RGB') + + img = qr_img + + # Save to bytes + img_io = io.BytesIO() + img.save(img_io, 'PNG') + img_io.seek(0) + + # Return as download or inline + filename = f"spool_{spool_id}_qr.png" if spool_id else f"tag_{tag_id}_qr.png" + return send_file(img_io, mimetype='image/png', as_attachment=download, download_name=filename) except Exception as e: traceback.print_exc() return render_template('error.html', exception=str(e)) @@ -487,4 +568,4 @@ def print_select_spool(): ) except Exception as e: traceback.print_exc() - return render_template('error.html', exception=str(e)) + return render_template('error.html', exception=str(e)) diff --git a/requirements.txt b/requirements.txt index 83ac12be..2de940bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ pycurl==7.45.6 gunicorn==23.0.0 pytest==8.3.4 python-dotenv==1.0.1 +qrcode[pil]==8.0 diff --git a/templates/assign_tag.html b/templates/assign_tag.html index 3dc71d84..a9773d02 100644 --- a/templates/assign_tag.html +++ b/templates/assign_tag.html @@ -2,6 +2,6 @@ {% block content %} -

Assign NFC Tag to Spool

+

Assign Tag to Spool

{% with action_assign=True, materials=materials, selected_materials=selected_materials %}{% include 'fragments/list_spools.html' %}{% endwith %} {% endblock %} diff --git a/templates/base.html b/templates/base.html index 13f3e977..7d8e5c6a 100644 --- a/templates/base.html +++ b/templates/base.html @@ -38,7 +38,7 @@
{{ PRINTER_MODEL["model"]}} - {{PRINTER_MOD diff --git a/templates/fragments/list_spools.html b/templates/fragments/list_spools.html index 5fed5608..326e1215 100644 --- a/templates/fragments/list_spools.html +++ b/templates/fragments/list_spools.html @@ -89,14 +89,19 @@
{% if spool.extra.get("tag") or spool.extra.get("tag") == "\"\"" %} - Reassign + Update NFC Tag {% else %} - Assign + Create NFC Tag {% endif %} + + + QR Code + + {% endif %} {% if action_assign_print %} diff --git a/templates/write_tag.html b/templates/write_tag.html index d49e7dc1..b8de186b 100644 --- a/templates/write_tag.html +++ b/templates/write_tag.html @@ -3,16 +3,21 @@ {% block content %}
-

NFC Write Process

-

Follow the steps below to write data to your NFC tag.

+

Tag Assignment

+

Choose between NFC tag or QR code for your spool.

- +
+
+
+ NFC Tag +
+
-
+
Instructions -
+
+ + +
+ +
+ + + - -
- + +
+
+
+ QR Code +
+
+
+

Print this QR code and attach it to your spool. Scan it with any smartphone camera to access spool information.

+ + +
+ QR Code for Spool {{ spool_id }} +
+ + + + Download QR Code Image + +
- -