Code cleanup: Separate spoolman_client

Bugfix: Unchanged extras must be sent to spoolman for them not to disappear
Bugfix: Remove active tray from inactive spools
Docs: Add note about the Base_URL
This commit is contained in:
Filip Bednárik 2024-12-13 11:01:41 +01:00
parent a56c78b24a
commit 3a6ecf8e9f
4 changed files with 84 additions and 51 deletions

View File

@ -40,11 +40,14 @@ How to setup:
- On the website pick the slot you put your filament in
- Done
Notes:
- If you change the BASE_URL of this app, you will need to reconfigure all NFC TAGS
TBD:
- Filament remaining in AMS (I have only AMS lite, if you have AMS we can test together)
- Filament spending based on printing
- Evidently needed GUI improvements
- Code cleanup
- Deployment as Docker and Helm chart
- Easy configuration via env properties
- Video showcase
- TODOs

44
app.py
View File

@ -1,16 +1,17 @@
import json
import uuid
import requests
from flask import Flask, request, render_template_string
from config import BASE_URL, PRINTER_ID, SPOOLMAN_API_URL
from config import BASE_URL
from filament import generate_filament_brand_code, generate_filament_temperatures
from messages import AMS_FILAMENT_SETTING
from mqtt_bambulab import fetchSpools, getLastAMSConfig, publish, getMqttClient
from mqtt_bambulab import fetchSpools, getLastAMSConfig, publish, getMqttClient, setActiveTray
from spoolman_client import patchExtraTags, getSpoolById
app = Flask(__name__)
@app.route("/spool_info")
def spool_info():
tag_id = request.args.get("tag_id")
@ -68,6 +69,7 @@ def spool_info():
return render_template_string(html)
@app.route("/tray_load")
def tray_load():
tag_id = request.args.get("tag_id")
@ -80,25 +82,15 @@ def tray_load():
try:
# Update Spoolman with the selected tray
response = requests.patch(f"{SPOOLMAN_API_URL}/spool/{spool_id}", json={
"extra": {
"tag": json.dumps(tag_id),
"active_tray": json.dumps(f"{PRINTER_ID}_{ams_id}_{tray_id}"),
}
})
print(response.status_code)
print(response.text)
spool_data = getSpoolById(spool_id)
response = requests.get(f"{SPOOLMAN_API_URL}/spool/{spool_id}")
print(response.status_code)
print(response.text)
setActiveTray(spool_id, spool_data["extra"], ams_id, tray_id)
spool_data = json.loads(response.text)
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"
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(",")
@ -112,11 +104,12 @@ def tray_load():
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", ""))
spool_data["filament"]["vendor"]["name"],
spool_data["filament"]["extra"].get("type", ""))
ams_message["print"]["tray_info_idx"] = filament_brand_code["brand_code"]
#ams_message["print"]["tray_sub_brands"] = filament_brand_code["sub_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)
@ -129,10 +122,10 @@ def tray_load():
except Exception as e:
return f"<h1>Error</h1><p>{str(e)}</p>"
@app.route("/")
def home():
try:
# Update Spoolman with the selected tray
spools = fetchSpools()
last_ams_config = getLastAMSConfig()
@ -176,6 +169,7 @@ def home():
except Exception as e:
return f"<h1>Error</h1><p>{str(e)}</p>"
@app.route("/assign_tag")
def assign_tag():
spool_id = request.args.get("spool_id")
@ -185,15 +179,10 @@ def assign_tag():
myuuid = str(uuid.uuid4())
resp = requests.patch(f"{SPOOLMAN_API_URL}/spool/{spool_id}", json={
"extra": {
"tag": json.dumps(myuuid),
}
patchExtraTags(spool_id, {}, {
"tag": json.dumps(myuuid),
})
print(resp.status_code)
print(resp.raw)
return f"""
<html>
<header>
@ -217,6 +206,7 @@ def assign_tag():
</html>
"""
@app.route('/', methods=['GET'])
def health():
return "OK", 200

View File

@ -4,15 +4,16 @@ import traceback
from threading import Thread
import paho.mqtt.client as mqtt
import requests
from config import PRINTER_ID, PRINTER_CODE, PRINTER_IP, SPOOLMAN_API_URL
from config import PRINTER_ID, PRINTER_CODE, PRINTER_IP
from messages import GET_VERSION, PUSH_ALL
from spoolman_client import fetchSpoolList, patchExtraTags
def num2letter(num):
return chr(ord("A") + int(num))
def publish(client, msg):
result = client.publish(f"device/{PRINTER_ID}/request", json.dumps(msg))
status = result[0]
@ -23,12 +24,14 @@ def publish(client, msg):
print(f"Failed to send message to topic device/{PRINTER_ID}/request")
return False
# Inspired by https://github.com/Donkie/Spoolman/issues/217#issuecomment-2303022970
def on_message(client, userdata, msg):
global LAST_AMS_CONFIG
# TODO: Consume spool
try:
data = json.loads(msg.payload.decode())
print(data)
if "print" in data and "vt_tray" in data["print"]:
print(data)
LAST_AMS_CONFIG["vt_tray"] = data["print"]["vt_tray"]
@ -42,7 +45,8 @@ def on_message(client, userdata, msg):
print(f"AMS [{num2letter(ams['id'])}] (hum: {ams['humidity']}, temp: {ams['temp']}ºC)")
for tray in ams["tray"]:
if "tray_sub_brands" in tray:
print(f" - [{num2letter(ams['id'])}{tray['id']}] {tray['tray_sub_brands']} {tray['tray_color']} ({str(tray['remain']).zfill(3)}%) [[ {tray['tag_uid']} ]]")
print(
f" - [{num2letter(ams['id'])}{tray['id']}] {tray['tray_sub_brands']} {tray['tray_color']} ({str(tray['remain']).zfill(3)}%) [[ {tray['tag_uid']} ]]")
found = False
for spool in SPOOLS:
@ -54,40 +58,45 @@ def on_message(client, userdata, msg):
found = True
resp = requests.patch(f"{SPOOLMAN_API_URL}/spool/{spool['id']}", json={
"extra": {
"tag": spool["extra"]["tag"],
"active_tray": json.dumps(f"{PRINTER_ID}_{ams['id']}_{tray['id']}"),
}
})
setActiveTray(spool['id'], spool["extra"], ams['id'], tray["id"])
print(resp.text)
print(resp.status_code)
#TODO: remove active_tray from inactive spools
#Doesn't work for AMS Lite
#requests.patch(f"http://{SPOOLMAN_IP}:7912/api/v1/spool/{spool['id']}", json={
# TODO: filament remaining - Doesn't work for AMS Lite
# requests.patch(f"http://{SPOOLMAN_IP}:7912/api/v1/spool/{spool['id']}", json={
# "remaining_weight": tray["remain"] / 100 * tray["tray_weight"]
#})
# })
if not found:
print(" - Not found. Update spool tag!")
if not found:
print(" - Not found. Update spool tag!")
except Exception as e:
traceback.print_exc()
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
print("Connected with result code " + str(rc))
client.subscribe(f"device/{PRINTER_ID}/report")
publish(client, GET_VERSION)
publish(client, PUSH_ALL)
def setActiveTray(spool_id, spool_extra, ams_id, tray_id):
patchExtraTags(spool_id, spool_extra, {
"active_tray": json.dumps(f"{PRINTER_ID}_{ams_id}_{tray_id}"),
})
# Remove active tray from inactive spools
for old_spool in SPOOLS:
if spool_id != old_spool["id"] and old_spool["extra"]["active_tray"] == json.dumps(
f"{PRINTER_ID}_{ams_id}_{tray_id}"):
patchExtraTags(old_spool["id"], old_spool["extra"], {"active_tray": json.dumps("")})
# Fetch spools from spoolman
def fetchSpools():
global SPOOLS
response = requests.get(f"{SPOOLMAN_API_URL}/spool")
SPOOLS = response.json()
SPOOLS = fetchSpoolList()
return SPOOLS
def async_subscribe():
global MQTT_CLIENT
MQTT_CLIENT = mqtt.Client()
@ -102,18 +111,22 @@ def async_subscribe():
MQTT_CLIENT.connect(PRINTER_IP, 8883)
MQTT_CLIENT.loop_forever()
# Start the asynchronous processing in a separate thread
thread = Thread(target=async_subscribe)
thread.start()
def getLastAMSConfig():
global LAST_AMS_CONFIG
return LAST_AMS_CONFIG
def getMqttClient():
global MQTT_CLIENT
return MQTT_CLIENT
MQTT_CLIENT = {} # Global variable storing MQTT Client
MQTT_CLIENT = {} # Global variable storing MQTT Client
LAST_AMS_CONFIG = {} # Global variable storing last AMS configuration
SPOOLS = fetchSpools() # Global variable storing latest spool from spoolman
SPOOLS = fetchSpools() # Global variable storing latest spool from spoolman

27
spoolman_client.py Normal file
View File

@ -0,0 +1,27 @@
import requests
from config import SPOOLMAN_API_URL
def patchExtraTags(spool_id, old_extras, new_extras):
for key, value in new_extras.items():
old_extras[key] = value
resp = requests.patch(f"{SPOOLMAN_API_URL}/spool/{spool_id}", json={
"extra": old_extras
})
print(resp.text)
print(resp.status_code)
def getSpoolById(spool_id):
response = requests.get(f"{SPOOLMAN_API_URL}/spool/{spool_id}")
print(response.status_code)
print(response.text)
return response.json()
def fetchSpoolList():
response = requests.get(f"{SPOOLMAN_API_URL}/spool")
print(response.status_code)
print(response.text)
return response.json()