Skip to content

Feature AMS Info Card#570

Open
cadtoolbox wants to merge 14 commits intomaziggy:0.2.2b1from
cadtoolbox:dev_amsinfocard
Open

Feature AMS Info Card#570
cadtoolbox wants to merge 14 commits intomaziggy:0.2.2b1from
cadtoolbox:dev_amsinfocard

Conversation

@cadtoolbox
Copy link
Contributor

Description

Adds two UX improvements to the AMS display on the Printers page: a hover popup on each AMS label exposing hardware details and a user-editable friendly name, and 1-based slot numbers centered inside each filament color circle with auto-inverted contrast.

AMS name hover popup
Hovering the AMS label (e.g. AMS-A) opens a popover showing:

  • Serial number – sourced from the MQTT sn field on the AMS unit object
  • Firmware version – parsed from info.module[name="ams/"].sw_ver in the get_version MQTT response
  • Friendly name – editable text field, stored in a new ams_labels DB table; gated by printers:update permission
  • Filament location will reflect this new AMS friendly name as a suffix (friendly_name)

Related Issue

Fixes #464

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test addition or update

Changes Made

Backend

  • New AmsLabel model (printer_id, ams_id, label); table auto-created via create_all.
  • AMSUnit schema gains serial_number and sw_ver fields; both populated in the status route from raw MQTT data.
    _handle_version_info in bambu_mqtt.py extended to parse ams/ modules and write sw_ver/sn back onto the stored AMS unit dict.

Frontend

  • New AmsNameHoverCard component in PrintersPage.tsx using the same hover-card pattern as NozzleSlotHoverCard.
  • AMSUnit interface extended with serial_number and sw_ver; three new API client functions added.
  • Slot numbers in color circles
  • Each filament circle now renders its 1-based slot number centered inside it. Font color is computed via the existing isLightFilamentColor helper—black text on light filament, white on dark. Applied consistently to regular 4-tray AMS, HT AMS, and external spool slots.

Bug Fixes

  • backend/app/api/routes/settings.py: After copying the backup database file, call reinitialize_database() to point the engine at the restored file, then init_db() to create missing tables (CREATE TABLE IF NOT EXISTS) and apply column migrations — eliminating the need for a manual restart to recover a usable schema.
# After shutil.copy2(backup_db, db_path)
await reinitialize_database()
await init_db()

Screenshots

2026-02-26_08-41-29

Testing

  • I have tested this on my local machine
  • I have tested with my printer model: X1E

Checklist

  • My code follows the project's coding style
  • I have commented my code where necessary
  • I have updated the documentation (if needed)
  • My changes generate no new warnings
  • I have tested my changes thoroughly

Additional Notes

@cadtoolbox cadtoolbox marked this pull request as ready for review March 2, 2026 14:32
@cadtoolbox cadtoolbox requested a review from maziggy as a code owner March 2, 2026 14:32
Copy link
Owner

@maziggy maziggy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few items to address before we can merge:

1. Runtime crash — missing printer_manager import (blocker)

backend/app/api/routes/inventory.py — list_assignments calls printer_manager.get_status(pid) but never imports it. This will NameError on every GET
/inventory/assignments request. The pattern used elsewhere in this file is an inline import:

async def list_assignments(...):
from backend.app.services.printer_manager import printer_manager
# ... rest of function

2. N+1 queries in get_ams_labels fallback loop

backend/app/api/routes/printers.py — The synthetic-key backward-compat loop issues one SELECT per AMS unit. With dual-AMS on older firmware (no serial reporting), that's up to 8 separate queries. Collect all synthetic keys first and use a single IN clause — same pattern as the real-serial query block right above it.

3. AMS version changes don't broadcast without OTA module

backend/app/services/bambu_mqtt.py — state_changed is only set True inside the OTA block, so if get_version contains AMS modules but no OTA entry, the serial/firmware
values get written to raw_data but on_state_change never fires. Fix: also set state_changed = True when caching AMS version data.

4. PUT endpoint should use JSON body instead of query params

backend/app/api/routes/printers.py + frontend/src/api/client.ts — save_ams_label passes the label text as a URL query parameter (?label=...). This shows up in server
access logs and is unconventional for mutations. Recommend switching to a Pydantic JSON body model — this also lets you add min_length=1 to reject empty labels at the
API boundary (currently ?label= stores a blank row).

5. Minor: simplify onBlur hover detection

frontend/src/pages/PrintersPage.tsx — element.matches(':hover') in the onBlur handler is unreliable in Firefox during focus transitions. Since handleMouseLeave already
guards on !isInputFocused, the onBlur can just clear the flag and let the mouse-leave timeout handle closing naturally.

@cadtoolbox
Copy link
Contributor Author

@maziggy Comments have been incorporated

@cadtoolbox cadtoolbox requested a review from maziggy March 3, 2026 01:18
Copy link
Owner

@maziggy maziggy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still a few items to address:

Bug — Hover card gets stuck open

When the user focuses the friendly name input and then clicks elsewhere, onBlur sets isInputFocused = false but doesn't check if the mouse already left the card. The popup stays open indefinitely until the user hovers back and leaves again.

Fix in AmsNameHoverCard:
onBlur={() => {
setIsInputFocused(false);
timeoutRef.current = setTimeout(() => setIsVisible(false), 200);
}}

"Clear" button UX

The "Clear" button only empties the input — the user still has to click "Save" to actually delete the label. Either rename it to "Reset" or have it call deleteAmsLabel directly and close the popup.

Warning logs may be noisy

_handle_version_info now logs warnings for every AMS unit missing serial/firmware after each get_version response. On older firmware that doesn't report these fields,
this will fire repeatedly. Consider debug level or warning once per AMS unit.

list_assignments performance

This endpoint now calls printer_manager.get_status() per unique printer + queries ams_labels on every Inventory page load. Not a blocker but worth keeping an eye on
with many printers.

Minor: Slot number rendering duplicated 4x

The filament circle + slot number JSX is copy-pasted across regular AMS, HT AMS, external spool, and printing state sections. A small FilamentSlotCircle component would
prevent drift between copies.

@cadtoolbox
Copy link
Contributor Author

@maziggy Please re-review

@cadtoolbox cadtoolbox requested a review from maziggy March 3, 2026 13:37
@maziggy
Copy link
Owner

maziggy commented Mar 3, 2026

Please rebase to resolve conflict.

@cadtoolbox
Copy link
Contributor Author

@maziggy You all work so fast it's hard to keep up. ;).

Isn't 0.2.2b1 the latest?

@cadtoolbox
Copy link
Contributor Author

@maziggy Are we good on changes for this one?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants