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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,9 @@ SpoolMan can print QR-code stickers for every spool; follow the SpoolMan label g
- set `PRINTER_ID` — find it in the printer settings under Setting → Device → Printer SN.
- set `PRINTER_ACCESS_CODE` — find it in Setting → LAN Only Mode → Access Code (the LAN Only Mode toggle may stay off).
- set `PRINTER_IP` — found in Setting → LAN Only Mode → IP Address.
- set `SPOOLMAN_BASE_URL` — the URL of your SpoolMan installation without trailing slash.
- set `SPOOLMAN_BASE_URL` — the URL of your SpoolMan installation without trailing slash. Used for UI links (menu, spool detail pages) and as the default for API calls when `SPOOLMAN_API_URL` is not set.
- set `SPOOLMAN_UI_URL` *(optional)* — semantic alias for `SPOOLMAN_BASE_URL`; either variable satisfies the requirement. If both are set, `SPOOLMAN_BASE_URL` takes precedence.
- set `SPOOLMAN_API_URL` *(optional)* — the URL OpenSpoolMan uses for backend API calls to SpoolMan (without trailing slash). Defaults to `SPOOLMAN_BASE_URL`. Useful for reverse-proxy / split-horizon setups where the container reaches SpoolMan via internal DNS (e.g. `http://spoolman:8000`) while the browser needs a public hostname (e.g. `https://spoolman.example.com`).
- set `AUTO_SPEND` to `True` to enable legacy slicer-estimate tracking (no live layer tracking).
- set `TRACK_LAYER_USAGE` to `True` to switch to per-layer tracking/consumption **while `AUTO_SPEND` is also `True`**. If `AUTO_SPEND` is `False`, all filament tracking remains disabled regardless of `TRACK_LAYER_USAGE`.
- set `AUTO_SPEND` to `True` if you want automatic filament usage tracking (see the AUTO SPEND notes below).
Expand Down
12 changes: 8 additions & 4 deletions config.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ PRINTER_IP=
PRINTER_NAME=
SPOOLMAN_BASE_URL=
AUTO_SPEND=False
TRACK_LAYER_USAGE=False
SPOOL_SORTING=filament.material:asc,filament.vendor.name:asc,filament.name:asc
CLEAR_ASSIGNMENT_WHEN_EMPTY=False
COLOR_DISTANCE_TOLERANCE=40
TRACK_LAYER_USAGE=False
SPOOL_SORTING=filament.material:asc,filament.vendor.name:asc,filament.name:asc
CLEAR_ASSIGNMENT_WHEN_EMPTY=False
COLOR_DISTANCE_TOLERANCE=40

# Allow API and UI URLs to differ (reverse-proxy friendly)
# SPOOLMAN_UI_URL= # Semantic alias for SPOOLMAN_BASE_URL
# SPOOLMAN_API_URL=http://spoolman:8000 # The Backend reaches Spoolman via internal Docker DNS
5 changes: 3 additions & 2 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ def _env_to_int(name: str, default: int) -> int:
PRINTER_CODE = os.getenv("PRINTER_ACCESS_CODE") # Printer access code - Run init_bambulab.py
PRINTER_IP = os.getenv("PRINTER_IP") # Printer local IP address - Check wireless on printer
PRINTER_NAME = os.getenv("PRINTER_NAME") # Printer name - Check wireless on printer
SPOOLMAN_BASE_URL = os.getenv("SPOOLMAN_BASE_URL")
SPOOLMAN_API_URL = f"{SPOOLMAN_BASE_URL}/api/v1"
SPOOLMAN_BASE_URL = os.getenv("SPOOLMAN_BASE_URL") or os.getenv("SPOOLMAN_UI_URL")
SPOOLMAN_API_URL = os.getenv("SPOOLMAN_API_URL") or SPOOLMAN_BASE_URL
SPOOLMAN_API_V1 = f"{SPOOLMAN_API_URL}/api/v1"
AUTO_SPEND = _env_to_bool("AUTO_SPEND", False)
TRACK_LAYER_USAGE = _env_to_bool("TRACK_LAYER_USAGE", False)
SPOOL_SORTING = os.getenv(
Expand Down
90 changes: 45 additions & 45 deletions spoolman_client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import requests
from config import SPOOLMAN_API_URL, SPOOL_SORTING
from config import SPOOLMAN_API_V1, SPOOL_SORTING
import json
from logger import append_to_rotating_file, log

Expand Down Expand Up @@ -31,7 +31,7 @@ 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={
resp = requests.patch(f"{SPOOLMAN_API_V1}/spool/{spool_id}", json={
"extra": old_extras
})
_log_spoolman_change(
Expand All @@ -45,35 +45,35 @@ def patchExtraTags(spool_id, old_extras, new_extras):


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():
if SPOOL_SORTING:
response = requests.get(f"{SPOOLMAN_API_URL}/spool?sort={SPOOL_SORTING}")
else:
response = requests.get(f"{SPOOLMAN_API_URL}/spool")
#print(response.status_code)
#print(response.text)
return response.json()
def consumeSpool(spool_id, use_weight=None, use_length=None):
if use_weight is None and use_length is None:
raise ValueError("use_weight or use_length is required")
payload = {}
response = requests.get(f"{SPOOLMAN_API_V1}/spool/{spool_id}")
#print(response.status_code)
#print(response.text)
return response.json()


def fetchSpoolList():
if SPOOL_SORTING:
response = requests.get(f"{SPOOLMAN_API_V1}/spool?sort={SPOOL_SORTING}")
else:
response = requests.get(f"{SPOOLMAN_API_V1}/spool")

#print(response.status_code)
#print(response.text)
return response.json()

def consumeSpool(spool_id, use_weight=None, use_length=None):
if use_weight is None and use_length is None:
raise ValueError("use_weight or use_length is required")

payload = {}
if use_weight is not None:
payload["use_weight"] = use_weight
if use_length is not None:
payload["use_length"] = use_length

log(f'Consuming {payload} from spool {spool_id}')

response = requests.put(f"{SPOOLMAN_API_URL}/spool/{spool_id}/use", json=payload)
response = requests.put(f"{SPOOLMAN_API_V1}/spool/{spool_id}/use", json=payload)
_log_spoolman_change(
"consume_spool",
spool_id=spool_id,
Expand All @@ -82,25 +82,25 @@ def consumeSpool(spool_id, use_weight=None, use_length=None):
)
#print(response.status_code)
#print(response.text)
def fetchSettings():
response = requests.get(f"{SPOOLMAN_API_URL}/setting/")
#print(response.status_code)
#print(response.text)
# JSON in ein Python-Dictionary laden
data = response.json()
# Extrahiere die Werte aus den relevanten Feldern
extra_fields_spool = json.loads(data["extra_fields_spool"]["value"])
extra_fields_filament = json.loads(data["extra_fields_filament"]["value"])
base_url = data["base_url"]["value"]
currency = data["currency"]["value"]
settings = {}
settings["extra_fields_spool"] = extra_fields_spool
settings["extra_fields_filament"] = extra_fields_filament
settings["base_url"] = base_url.replace('"', '')
settings["currency"] = currency.replace('"', '')

def fetchSettings():
response = requests.get(f"{SPOOLMAN_API_V1}/setting/")
#print(response.status_code)
#print(response.text)

# JSON in ein Python-Dictionary laden
data = response.json()

# Extrahiere die Werte aus den relevanten Feldern
extra_fields_spool = json.loads(data["extra_fields_spool"]["value"])
extra_fields_filament = json.loads(data["extra_fields_filament"]["value"])
base_url = data["base_url"]["value"]
currency = data["currency"]["value"]

settings = {}
settings["extra_fields_spool"] = extra_fields_spool
settings["extra_fields_filament"] = extra_fields_filament
settings["base_url"] = base_url.replace('"', '')
settings["currency"] = currency.replace('"', '')

return settings