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
123 changes: 107 additions & 16 deletions app.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -370,12 +373,100 @@ def write_tag():
if not spool_id:
return render_template('error.html', exception="spool ID is required as a query parameter (e.g., ?spool_id=1)")

myuuid = str(uuid.uuid4())
# Check if tag already exists
spool_data = spoolman_client.getSpoolById(spool_id)
tag_value = spool_data.get("extra", {}).get("tag")

if tag_value:
# Use existing tag
myuuid = json.loads(tag_value)
else:
# Create new tag
myuuid = str(uuid.uuid4())
existing_extra = spool_data.get("extra", {})
spoolman_client.patchExtraTags(spool_id, existing_extra, {
"tag": json.dumps(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))

spoolman_client.patchExtraTags(spool_id, {}, {
"tag": json.dumps(myuuid),
})
return render_template('write_tag.html', myuuid=myuuid)
@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))
Expand Down Expand Up @@ -487,4 +578,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))
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion templates/assign_tag.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

{% block content %}
<!-- Page Title -->
<h1 class="mb-4 text-center">Assign NFC Tag to Spool</h1>
<h1 class="mb-4 text-center">Assign Tag to Spool</h1>
{% with action_assign=True, materials=materials, selected_materials=selected_materials %}{% include 'fragments/list_spools.html' %}{% endwith %}
{% endblock %}
2 changes: 1 addition & 1 deletion templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ <h5 style="margin: 0px 5px 5px 5px;">{{ PRINTER_MODEL["model"]}} - {{PRINTER_MOD

<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
<li><a href="{{ url_for('home') }}" class="nav-link px-2 link-body-emphasis">Home</a></li>
<li><a href="{{ url_for('assign_tag') }}" class="nav-link px-2 link-body-emphasis">Assign NFC Tag</a></li>
<li><a href="{{ url_for('assign_tag') }}" class="nav-link px-2 link-body-emphasis">Assign Tag</a></li>
<li><a href="{{ url_for('print_history') }}" class="nav-link px-2 link-body-emphasis">Print History</a></li>
<li><a href="{{ SPOOLMAN_BASE_URL }}" target="_blank" class="nav-link px-2 link-body-emphasis">SpoolMan</a></li>
</ul>
Expand Down
14 changes: 6 additions & 8 deletions templates/fragments/list_spools.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,14 @@ <h6 class="mb-0">

{% if action_assign %}
<a href="{{ url_for('write_tag', spool_id=spool.id) }}" class="spool-action-link">
<!-- Action Icon -->
{% if spool.extra.get("tag") or spool.extra.get("tag") == "\"\"" %}
<span class="badge bg-secondary rounded-pill">
<i class="bi bi-plus-circle"></i> Reassign
</span>
{% else %}
<span class="badge bg-primary rounded-pill">
<i class="bi bi-plus-circle"></i> Assign
<i class="bi bi-tag"></i> Create NFC Tag
</span>
</a>
<a href="{{ url_for('qr_code', spool_id=spool.id) }}?download=true" class="spool-action-link" title="Download QR Code">
<span class="badge bg-info rounded-pill">
<i class="bi bi-qr-code"></i> QR Code
</span>
{% endif %}
</a>
{% endif %}

Expand Down
60 changes: 46 additions & 14 deletions templates/write_tag.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,69 @@
{% block content %}
<!-- Page Title -->
<div class="text-center mb-4">
<h1 class="fw-bold">NFC Write Process</h1>
<p class="text-muted">Follow the steps below to write data to your NFC tag.</p>
<h1 class="fw-bold">Tag Assignment</h1>
<p class="text-muted">Choose between NFC tag or QR code for your spool.</p>
</div>

<!-- Instruction Section -->
<!-- NFC Section -->
<div class="card shadow-sm mb-4">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="bi bi-nfc text-success me-2"></i> NFC Tag
</h5>
</div>
<div class="card-body">
<h5 class="card-title mb-3">
<h6 class="card-subtitle mb-3">
<i class="bi bi-info-circle text-info me-2"></i> Instructions
</h5>
</h6>
<ul class="list-unstyled">
<li><i class="bi bi-check-circle-fill text-success me-2"></i> Attach NFC tag to spool so you can reach it with the
top of your phone.
</li>
<li><i class="bi bi-wifi text-primary me-2"></i> Allow NFC usage if prompted.</li>
<li><i class="bi bi-phone text-secondary me-2"></i> Bring the tag close to your phone when prompted.</li>
</ul>

<!-- Action Button -->
<div class="text-center mt-3">
<button id="write" class="btn btn-lg btn-success shadow" onclick="writeNFC()">
<i class="bi bi-nfc me-2"></i> Start Writing NFC Tag
</button>
</div>

<!-- Message Display -->
<div id="message" class="alert alert-secondary text-center mt-3" role="alert">
Press "Start Writing NFC Tag" to begin.
</div>
</div>
</div>

<!-- Action Button -->
<div class="text-center mb-3">
<button id="write" class="btn btn-lg btn-primary shadow" onclick="writeNFC()">
<i class="bi bi-nfc me-2"></i> Start Writing NFC Tag
</button>
<!-- QR Code Section -->
<div class="card shadow-sm mb-4">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="bi bi-qr-code text-primary me-2"></i> QR Code
</h5>
</div>
<div class="card-body text-center">
<p class="text-muted mb-3">Print this QR code and attach it to your spool. Scan it with any smartphone camera to access spool information.</p>

<!-- QR Code Image -->
<div class="mb-3">
<img src="{{ url_for('qr_code', spool_id=spool_id, tag_id=myuuid) }}"
alt="QR Code for Spool {{ spool_id }}"
class="img-fluid"
style="max-width: 300px;">
</div>

<!-- Download Button -->
<a href="{{ url_for('qr_code', spool_id=spool_id, tag_id=myuuid, download='true') }}"
class="btn btn-primary">
<i class="bi bi-download me-2"></i> Download QR Code Image
</a>
</div>
</div>

<!-- Message Display -->
<div id="message" class="alert alert-secondary text-center" role="alert">
Press "Start Writing NFC Tag" to begin.
</div>

<script type="text/javascript">
function writeNFC() {
Expand Down