- Python 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| spoolman_audit | ||
| tests | ||
| .env.example | ||
| .gitignore | ||
| .pre-commit-config.yaml | ||
| prices.json | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
Spoolman Audit
Link a set of USB scales, a USB barcode scanner (serial or keyboard emulation), and a Spoolman instance.
Scan a Spoolman spool QR code → wait for a stable scale reading → send the gross weight to Spoolman (PUT /api/v1/spool/{id}/measure). Spoolman subtracts the configured empty-spool weight and updates remaining filament.
Setup
Requires uv.
uv sync
copy .env.example .env
uv run pre-commit install
Edit .env with your Spoolman URL and hardware settings. Serial ports look like COM18 on Windows and /dev/cu.usbmodem14101 on macOS/Linux. Set SCANNER_INPUT_MODE=keyboard when the scanner uses keyboard emulation; SCANNER_PORT is then optional.
Run
Opens the CustomTkinter desktop UI by default:
uv run spoolman-audit
Console-only mode:
uv run spoolman-audit --headless
Demo mode (no hardware)
Mocks the scanner, scales, and Spoolman so you can exercise the UI locally:
uv run spoolman-audit --demo
Use Place on scales then Scan with spool IDs 1, 2, or 3 (sample mock spools). Same via env: DEMO_MODE=true.
Individual mocks: SCANNER_MOCK, SCALES_MOCK, SPOOLMAN_MOCK. With only SCALES_MOCK=true, the UI still shows Place on scales / Clear scales.
Development
uv sync --group dev
uv run ruff check .
uv run ruff format .
A git pre-commit hook runs Ruff (check with autofix, then format) on staged files. Install it once with uv run pre-commit install.
Flow
- Resting overlay asks you to place filament on the scales (≥100 g), then scan the inventory QR code.
- App listens on the scanner serial port or captures keyboard-emulated scans anywhere in its window.
- On a
web+spoolman:s-{id}barcode, it fetches the spool from Spoolman. - If the scales are below 100 g, it shows the spool in info mode without updating its weight.
- Otherwise, it waits until the scales report a stable weight for
SCALES_STABLE_SECONDS. - That gross weight (grams) is posted to Spoolman’s measure endpoint.
- Remaining weight from the API response is shown for 60 seconds, then the UI returns to the resting overlay.
Scales protocol
Expected frame shape (from the original prototype):
<stability>\r<weight>\r<unused>\r\n
Stability byte 0x00 means stable; non-zero means unstable. Weight is parsed as grams.
Pricing
Tier rates live in prices.json (member and non-member price per gram). Filament tier is read from Spoolman’s filament extra.tier field and shown in the UI with rates when defined.
Settings
| Variable | Required | Default | Meaning |
|---|---|---|---|
SPOOLMAN_URL |
yes | Base URL, e.g. http://192.168.3.6:7912 |
|
SCANNER_INPUT_MODE |
no | serial |
serial or keyboard |
SCANNER_PORT |
yes* | Scanner serial port | |
SCALES_PORT |
yes* | Scales serial port | |
SCALES_MOCK |
no | false |
Random weights; skips real scales |
SCANNER_BAUDRATE |
no | 9600 |
|
SCALES_BAUDRATE |
no | 9600 |
|
SCALES_STABLE_SECONDS |
no | 1.0 |
How long weight must stay stable |
SCALES_TIMEOUT_SECONDS |
no | 10.0 |
Give up waiting for a stable reading |
* SCANNER_PORT is only required in serial mode. SCALES_PORT is not required when SCALES_MOCK=true.