Narrowed CSS selector to only target max-w-2xl modals (Assign Spool)
not max-w-sm ones (slot action picker). Removed inner max-h-96 on
the spool grid to eliminate nested scrollbars.
CSS rules scoped via data-spoolbuddy-kiosk force the modal panel to
90vh with flex column layout. Content area scrolls, header/footer
pinned. Uses height (not just max-height) so flex-basis auto works
correctly. No flash — CSS applies before paint.
useEffect in SpoolBuddyAmsPage applies inline styles to the modal
panel after render (requestAnimationFrame + setTimeout) to cap it
at 90vh with scrollable content. Targets the DOM directly to avoid
CSS specificity issues with Tailwind v4.
Used a ref callback wrapper in SpoolBuddyAmsPage to directly apply
inline styles (max-height 90vh, flex column, scrollable content) to
the shared AssignSpoolModal DOM elements without modifying the
shared component.
Added data-spoolbuddy-kiosk attribute to the SpoolBuddy layout and
global CSS rules that cap modals at 90vh with scrollable content on
the small touchscreen, without modifying shared components.
Added scoped CSS in SpoolBuddyAmsPage that constrains the shared
AssignSpoolModal to 90vh with scrollable content and pinned
header/footer, without modifying the shared component.
Added scoped CSS in SpoolBuddyAmsPage that constrains the shared
AssignSpoolModal to 90vh with scrollable content, without modifying
the shared component used by the main Bambuddy frontend.
The NFC Diag and Scale Diag buttons on the Write Tag page's right
card were misplaced — diagnostics are already available on the
Settings page. Removed the buttons and all associated dead code.
When the virtual keyboard opened on the SpoolBuddy device tab, the
API token field and Save button were hidden behind the keyboard.
The keyboard now adds bottom scroll padding and scrolls the parent
card (not just the focused input) to the top of the visible area,
keeping all related fields accessible.
When the virtual keyboard opened on the SpoolBuddy device tab, the
Backend URL and API Token fields were hidden behind the keyboard
overlay. The keyboard now adds temporary bottom padding to the
scrollable container so inputs scroll above it, and removes the
padding on dismiss.
Non-passive touchmove listener with preventDefault() blocks Chromium's
compositor-level back/forward swipe gesture. Once horizontal movement
is detected, the gesture is locked and all further touchmove events
are prevented for that swipe. Removed touch-action: pan-y which was
insufficient on its own.
Swiping left/right on the SpoolBuddy touchscreen now cycles through
online printers instead of triggering browser back/forward navigation.
Uses touch-action: pan-y to suppress Chromium's native swipe history
gesture. Auto-navigates to dashboard when a new NFC tag is detected
on any other page, with edge detection so a persistent tag doesn't
block manual navigation. Also wakes the screen if blanked.
Swiping left/right on the SpoolBuddy touchscreen now cycles through
online printers instead of triggering browser navigation. Auto-navigates
to dashboard when a new NFC tag is detected on any other page, with
edge detection so a persistent tag doesn't block manual navigation.
When an NFC tag is first detected while the UI is on a non-dashboard
page, automatically navigate to the main dashboard. Uses edge detection
(previous ref) so a persistently present tag doesn't block navigation
to other pages. Also wakes the screen if the display was blanked.
When an NFC tag is detected while the UI is on a non-dashboard page
(Settings, AMS, Write Tag, Calibration), automatically navigate back
to the main dashboard to display the scanned spool. Also wakes the
screen if the display blank timeout had kicked in.
The read_tag.py diagnostic script had stale PN5180 NTAG methods: TX CRC
was off (should be on), no Crypto1 clear, no IDLE→TRANSCEIVE state
reset. Multi-batch reads failed because the PN5180 enters an
unrecoverable state after an NTAG READ — requires a full GPIO hardware
reset between 4-page batches. Also rejected SAK 0x04 as unsupported,
and failed hard when reading past the end of smaller tags (MIFARE
Ultralight has 16 pages vs NTAG's 44+). Synced write methods with
daemon.
The read_tag.py diagnostic script had stale PN5180 NTAG methods: TX CRC
was off (should be on), no Crypto1 clear, no IDLE→TRANSCEIVE state
reset. Multi-batch reads failed because the PN5180 enters an
unrecoverable state after an NTAG READ — requires a full GPIO hardware
reset between 4-page batches. Also rejected SAK 0x04 as unsupported.
Synced write methods with daemon.
The read_tag.py diagnostic script had stale PN5180 NTAG methods: TX CRC
was off (should be on), no Crypto1 clear, no IDLE→TRANSCEIVE state
reset, and multi-batch reads failed because the PN5180 can't issue
consecutive NTAG READs without a full RF power cycle. Added extended-
timing reactivation (50ms gaps vs 10ms) between 4-page batches. Also
rejected SAK 0x04 as unsupported. Synced write methods with daemon.
The read_tag.py diagnostic script had stale PN5180 NTAG methods: TX CRC
was off (should be on), no Crypto1 clear, no IDLE→TRANSCEIVE state
reset, and multi-batch reads failed because subsequent READ commands
need a full state machine reset between batches. Also rejected SAK 0x04
as unsupported. Synced register setup and write methods with daemon.
The read_tag.py diagnostic script had stale PN5180 NTAG methods: TX CRC
was off (should be on), no Crypto1 clear, no IDLE→TRANSCEIVE state
reset, and the PN5180 drops the card after each READ batch requiring
reactivation between 4-page reads. Also rejected SAK 0x04 as
unsupported. Synced register setup with daemon and added per-batch
card reactivation.
The read_tag.py diagnostic script had stale PN5180 NTAG methods that
were never synced with the daemon's fixes: TX CRC was off (should be
on), no Crypto1 clear, no IDLE→TRANSCEIVE state reset, and unreliable
ACK/verification logic. Also rejected SAK 0x04 as unsupported. Synced
ntag_read_pages, ntag_write_page, and ntag_write_pages with daemon.
The read_tag.py diagnostic script only accepted SAK 0x00 for NTAG,
showing "Unsupported tag type" for chips reporting SAK 0x04 (MIFARE
Ultralight family). The daemon already handled both values — the
diagnostic was missed. Now accepts both 0x00 and 0x04.
usage, OS info, and system uptime every heartbeat using stdlib-only reads
from /proc and /sys. Stats are sent as a JSON blob in the heartbeat
payload, stored in a new system_stats TEXT column, and displayed in a
new "System" tab in SpoolBuddy Settings with color-coded usage bars.
usage, OS info, and system uptime every heartbeat using stdlib-only reads
from /proc and /sys. Stats are sent as a JSON blob in the heartbeat
payload, stored in a new system_stats TEXT column, and displayed in a
new "System" tab in SpoolBuddy Settings with color-coded usage bars.
The daemon now collects CPU temp, core count, load average, memory/disk
usage, OS info, and system uptime every heartbeat using stdlib-only reads
from /proc and /sys. Stats are sent as a JSON blob in the heartbeat
payload, stored in a new system_stats TEXT column, and displayed in a
new "System" tab in SpoolBuddy Settings with color-coded usage bars.
The daemon now collects CPU temp, core count, load average, memory/disk
usage, OS info, and system uptime every heartbeat using stdlib-only reads
from /proc and /sys. Stats are sent as a JSON blob in the heartbeat
payload, stored in a new system_stats TEXT column, and displayed in a
new "System" tab in SpoolBuddy Settings with color-coded usage bars.
Assigning or unassigning a spool now broadcasts a spool_assignment_changed
event to all connected WebSocket clients. The frontend handles this event
by invalidating the spool-assignments and slotPresets caches, so other
open tabs update automatically without a page reload.
The AssignToAmsModal was missing all material/profile validation guards
that exist in the main AssignSpoolModal. Clicking an AMS slot now checks
the spool's material and slicer profile against the target slot's current
filament, showing a confirmation dialog on mismatch. Respects the global
disable_filament_warnings setting. Uses existing i18n keys and ConfirmModal.
Some P1S/P1P firmware reports stg_cur=0 when idle, which maps to
the "Printing" stage name and overrides the correct IDLE gcode_state
on the printer card. Extend the existing A1/A1 Mini stg_cur idle bug
workaround to also cover P1S and P1P models.
Strip @mentions from changelog text in docker-publish-daily-beta.sh
so GitHub doesn't auto-generate a "Contributors" section in release
notes. Add --generate-notes=false for extra safety. Also add ports
2024-2026 (A1/P1S proprietary) to the docker-compose.yml bridge-mode
port mapping and update the install script comment.
Two issues prevented recovery from stale printer MQTT connections:
1. The spurious-disconnect filter (30s window) could suppress real
keepalive-timeout disconnects (which fire at ~22.5s with keepalive=15s).
Now checks rc.is_failure so error disconnects are never suppressed,
and tightens the window from 30s to 10s.
2. Stale detection (60s no messages) set state.connected=False but left
the TCP socket open, so paho kept publishing into the void. Now
force-closes the socket so paho's loop thread detects the break and
auto-reconnects.
When Spoolman is enabled, the sidebar filament item embedded
Spoolman at its root URL. Now navigates to <url>/spool so users
land directly on the spool management page.
When a printer enters FINISH/FAILED without Bambuddy ever seeing
RUNNING (e.g. after a restart), mark _completion_triggered = True
so the flag is clean for the next print cycle.
When a spool is already assigned/linked, only show Unassign/Unlink.
When no spool is mapped, only show Assign/Link. Previously both
buttons appeared simultaneously.
Clicking an AMS slot now shows a picker with options: Configure AMS
Slot, Assign Spool (inventory) or Link to Spoolman, and Unassign/
Unlink when a mapping exists. Reuses the existing AssignSpoolModal
and LinkSpoolModal components from the main Bambuddy frontend.
The PN5180 cannot read more than 4 NTAG pages after a batch write:
the second READ command (page 8+) returns rx_status=0 regardless of
state machine reset strategy. The write itself succeeds (tag ACKs
via SOF on every page). Remove verification and trust the writes.
Repeated IDLE→TRANSCEIVE resets inside the read loop broke the card
session after the first READ (page 8 returned rx_status=0). Match
the activate_type_a pattern: IDLE→TRANSCEIVE once before the loop,
set_transceive_mode() for subsequent iterations.
The verification read after writing failed because ntag_read_pages()
used set_transceive_mode() which was a no-op when already in
TRANSCEIVE. Apply the same IDLE→TRANSCEIVE reset pattern used in the
write path, clear Crypto1, and add diagnostic logging.
IRQ logging revealed the tag IS responding (RX_SOF_DET set) but the
PN5180 cannot capture the 4-bit ACK as a complete frame — RX_IRQ
never fires and RX_STATUS stays zero. Skip per-page ACK checking
and rely on the read-back verification in ntag_write_pages().
The PN5180 transceive state machine wasn't being reset between the
SELECT (from reactivate_card) and the WRITE command — just re-setting
the TRANSCEIVE bits was a no-op. Use IDLE→TRANSCEIVE transition like
activate_type_a does. Also clear Crypto1 bit and add IRQ status
logging.
Temporary diagnostics to identify which step of the NTAG write
fails: per-page ACK status, reactivation, read-back, or data
mismatch. Enable DEBUG level for pn5180 module.
The NTAG WRITE ACK is 4 bits (0x0A), not a full byte. The PN5180
RX_STATUS register reports 0 complete bytes with 4 extra bits, but
the check only looked at the byte count — returning False on every
successful write. Check both byte and bit fields of RX_STATUS.
Also bump post-write delay from 5ms to 10ms for margin.
Three issues:
1. SAK gate too strict — NTAG chips can report SAK 0x04 (MIFARE
Ultralight family) instead of 0x00. Accept both in nfc_reader.py
and main.py.
2. TX CRC disabled for NTAG WRITE — the NTAG spec requires CRC on
the WRITE command frame. Enable TX CRC in ntag_write_page().
3. TX CRC disabled for NTAG READ — the post-write verification read
also failed because ntag_read_pages() sent the READ command
without CRC. Enable TX CRC there too.
Two issues:
1. SAK gate too strict — NTAG chips can report SAK 0x04 (MIFARE
Ultralight family) instead of 0x00. Accept both in nfc_reader.py
and main.py.
2. TX CRC disabled during WRITE — the NTAG WRITE command (0xA2)
requires a CRC on the frame. The PN5180 was sending without CRC,
causing the tag to NAK every write. Enable TX CRC for writes
(RX CRC stays off for the 4-bit ACK).
NTAG 213/215/216 chips can report SAK 0x04 (MIFARE Ultralight family)
instead of 0x00 during anticollision. Accept both values for NTAG
detection and write operations in nfc_reader.py and main.py.
Remove branding/about card, fold Device ID into Device Info card,
place Backend/Auth config and diagnostic buttons side by side in a
2-column grid, remove redundant online/offline status from Device
Info, and tighten spacing throughout.