mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 19:21:33 +02:00
ed1683fe3e9e7c3880c8ee2d2bf7cb187c3d97bb
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7b4c5b3c1f |
fix(vp): show target printer's serial on proxy-mode VP card
The runtime services (SSDP, MQTT bind identity, cert subject) already advertise the target printer's serial via target_printer_serial or self.serial in proxy mode, but the API response that drives the VP settings card always returned the self-generated suffix-based serial. The card therefore displayed a serial that didn't match what slicers see, breaking the "one identity per VP" mental model. _vp_to_dict now resolves vp.target_printer_id -> Printer.serial_number when mode == VP_MODE_PROXY and substitutes the result into the response serial field. Archive / queue / review keep the self-generated serial (those modes never speak the target's identity). Orphaned target falls back to self-generated so the card still renders. |
||
|
|
19073f5c84 |
fix(vp): auto-derive access code from target printer in non-proxy modes
Non-proxy VPs (Archive / Review / Queue) with a target printer set up a live-mirror bridge that forwards the slicer's MQTT and RTSPS auth bytes through to the real printer. The slicer holds one code in its profile (the one it bound the VP with), and that code has to satisfy both the VP listener and the real printer at the far end of the bridge. If the codes diverge the bridge silently fails at the second hop — slicer reaches .49:8883, FINs before sending a ClientHello, retries identically. The wiki framed the code-match requirement as a camera-only concern; it isn't, all bridged protocols inherit. Fix removes the foot-gun instead of re-documenting it. When a target is selected on a non-proxy VP the access-code field switches to a read-only display showing the target's code with an Eye-toggle reveal; the backend auto-inherits on every create / update (any explicit access_code submitted alongside a target is silently overridden as belt-and-braces for non-UI clients). The required-when- enabling check now treats target-set as satisfying the access-code requirement. Standalone (no-target) non-proxy VPs still get the editable input + Save button. One-shot startup migration corrects any pre-existing mismatched rows: SELECTs diverged VPs and logs one INFO line per row for the audit trail, then UPDATEs via correlated subquery. Idempotent and portable between SQLite and Postgres. |
||
|
|
5d6d928b3f |
fix(virtual-printer): #1429 net.info[*].ip cache leak + mode wire-value rename
#1429 (reported by @TrickShotMLG02, confirmed by @Mape6 on a flat single-LAN that rules out subnet / mDNS-reflector theories): with the physical printer off the slicer's "Send" landed in Bambuddy's archive; once the printer powered on every subsequent "Send" went straight to the printer's SD card and bypassed Bambuddy. Bundle analysis: mape6-before showed clean FTP receive + archive lines, mape6-after had zero FTP attempts to Bambuddy once the printer was online. Cause: mqtt_bridge.py::_resolve_client encoded _target_ip_uint32_le / _vp_ip_uint32_le ONLY on client-identity change and early-returned on every refresh tick when the same client object was still bound. If target_client.ip_address was empty at first bind (DB row stale, or client constructed before SSDP refresh filled it in), the encoding stayed None, the net.info[*].ip rewrite block was skipped, the cache filled with the real printer IP, sticky-key preservation kept the poisoned net value alive across every subsequent incremental push, and the slicer followed the leaked IP. Only Bambuddy-restart-with-printer-off cleared it — the workaround both reporters independently arrived at. Same shape on multi-NIC printers (X1C, H2D Pro): the rewrite only matched entries whose ip equalled _target_ip_uint32_le, so a secondary interface IP Bambuddy never saw would leak through unchanged. Bridge fix: - _resolve_client calls a new _refresh_ip_encoding() on every refresh tick, even when client identity is unchanged; self-heals once ip_address becomes valid. - _refresh_ip_encoding() sweeps the existing _latest_print_state when encoding becomes valid for the first time. Without the sweep, sticky-key preservation keeps the pre-arm poisoned cache alive forever — incremental pushes that don't include net carry the bad value forward. - _rewrite_net_info_ips() rewrites EVERY non-zero net.info[].ip entry that doesn't already equal the VP IP, not just entries matching _target_ip_uint32_le. Multi-NIC printers stop leaking secondary interfaces. Zero-IP placeholders are left alone so "active interface" detection still works. - INFO logging on encoding arm/update and on cache sweep so future bundles directly answer "did the rewrite fire?". Mode wire-value rename (#1429 follow-up, separate confusion source): - Both reporters' support bundles showed mode: immediate while the UI said "Archive"; @TrickShotMLG02 quoted: "I have no idea why it says immediate in the support-info.json file. In the webui the printer is set to archive". UI button "Archive" had always saved immediate, and "Queue" had always saved print_queue. Canonical wire values are now archive / review / queue / proxy, matching the button labels 1:1. - New normalize_vp_mode() + VP_MODE_* constants in models/virtual_printer.py; manager.py normalises on construction so a legacy row read pre-migration still dispatches correctly. - core/database.py::run_migrations rewrites existing virtual_printers and settings rows; idempotent (re-runs are no-ops); identical SQL under SQLite and Postgres. - API routes accept both legacy and canonical on input, normalise before storage. GET /settings/virtual-printer normalises on read so the frontend's mode-button highlight works for stale legacy values. - Three frontend VP components (VirtualPrinterSettings, VirtualPrinterCard, VirtualPrinterAddDialog) switched click handlers and type aliases to canonical; each got its own normalizeMode() helper so a stale-cached settings payload still highlights the right button. Two pre-existing `printer.mode === 'queue' ? 'review'` legacy mappings in VirtualPrinterCard were the source of a test failure caught mid-implementation where the new canonical 'queue' was being mis-aliased back to 'review' and hiding the auto-dispatch + force-color-match toggles. mode handler is NOT the dispatch bug: manager.py::_archive_file (the handler for archive mode) doesn't dispatch to the physical printer. The "files end up on the printer's SD card" symptom was the IP-leak from the bridge cache. The mode rename is purely clarity / support- bundle accuracy. |
||
|
|
6bc6a1d683 |
feat(virtual-printer): setup diagnostic + one-click slicer-certificate export
Two recurring virtual-printer support pains, both on the Virtual Printers
settings page.
Setup check: a stethoscope action on each VP card runs a pass/fail/warn/skip
checklist — VP enabled, services running, bind interface still exists, access
code set, target printer (proxy mode), and a live TCP probe of the FTP / MQTT
/ discovery ports on the bind IP. start_server swallows per-service bind
errors, so a service object can exist while nothing is listening; probing the
bind IP from outside is the only reliable signal and it catches the common
"VP not visible in the slicer" bind-IP-conflict and stale-interface cases.
Slicer certificate: virtual printers present a TLS cert signed by a shared CA
the slicer must trust. Until now users had to docker exec in and cat
bbl_ca.crt. A "Slicer certificate" row on the settings card now offers Copy
and Download (bambuddy-virtual-printer-ca.crt) plus the SHA-256 fingerprint.
GET /virtual-printers/ca-certificate returns only the public certificate; the
CA private key never leaves the backend. The CA is generated on demand so the
button works before the first VP is enabled.
Backend:
- services/virtual_printer/diagnostic.py — run_vp_diagnostic + port probes
- schemas/virtual_printer.py — VPDiagnosticResult
- CertificateService.get_ca_certificate_info() + manager helper
- routes: GET /virtual-printers/ca-certificate, /{vp_id}/diagnostic
Frontend:
- VirtualPrinterDiagnosticModal.tsx; stethoscope button on VirtualPrinterCard
- caCert row on VirtualPrinterList; utils/clipboard.ts (shared copy w/
non-secure-context fallback + downloadTextFile), de-duplicating the
existing FQDN-copy logic
- vpDiagnostic.* + virtualPrinter.caCert.* across all 9 locales
9 backend unit tests + 4 route integration tests + 6 frontend tests.
Backend ruff clean, frontend build clean, i18n parity green.
|
||
|
|
64899a8ca4 |
refactor(virtual-printer): drop Tailscale LE cert path, keep toggle informational
The Tailscale toggle was supposed to obtain a publicly-trusted Let's Encrypt
cert via `tailscale cert` so users wouldn't need to import Bambuddy's CA into
the slicer. End-to-end testing showed this was always going to fail:
- Bambu Studio and OrcaSlicer refuse hostname input in the Add Printer
dialog (IP-only).
- Their printer-MQTT trust path validates only against the bundled BBL CA
store (`printer.cer`), NOT the system trust store. Confirmed against
ClusterM/open-bambu-networking's clean-room reimplementation:
`mosquitto_tls_set(BBL_CA)` + `verify_peer=1` + `tls_insecure=true` —
chain validation against BBL CA only, hostname check intentionally
skipped (because Bambu's printer cert CN is the device serial).
- LE certs don't chain to BBL CA, so the slicer rejects with the
well-known "-1" before any hostname/IP logic runs.
The cert-import step is unavoidable; LE provisioning was dead code for slicer
connections. Pivot:
- Toggle stays as an informational marker — when ON, the VP card surfaces
the host's Tailscale IP + MagicDNS hostname so users know what to paste
into the slicer.
- Cert is always self-signed (signed by `bbl_ca`).
- Tailscale exposure is via the existing bind_ip dropdown, which already
includes `tailscale0` IPs.
- Tailscale's role is strictly network reach — same trust burden as LAN.
Backend cuts:
- `tailscale.py`: `provision_cert`, `ensure_cert`, `cert_needs_renewal`,
`_FQDN_RE`, `_HTTPS_DISABLED_RE`, `TS_CERT_EXPIRY_THRESHOLD_DAYS`,
`cryptography` import. Keep `get_status` and `TailscaleStatus`.
- `certificate.py`: `ts_cert_path`, `ts_key_path`, `use_tailscale_cert`.
- `manager.py`: `tailscale_fqdn` field, `_cert_renewal_task`,
`_cert_restart_task`, `_cert_renewal_loop`, `_restart_for_cert_renewal`,
`_cancel_renewal_task`, `_cancel_restart_task`. Simplify
`_resolve_cert_and_advertise` to a sync method that just generates the
self-signed cert. Drop `tailscale_disabled` from the change-detection
diff (toggle is informational — no service restart needed).
- `routes/virtual_printers.py` + `routes/settings.py`: drop the
`tailscale_not_available` 409 guard on toggle-enable.
Frontend cuts:
- `VirtualPrinterCard.tsx`: FQDN/IP display sourced from
`multiVirtualPrinterApi.getTailscaleStatus()` (host-level) when toggle
is ON, instead of `printer.status.tailscale_fqdn` (cert side-effect,
no longer populated). Drop the `tailscale_not_available` toast handler.
- `api/client.ts`: drop `tailscale_fqdn` from the VP status type.
- i18n: rewrite `tailscaleDisabled.description` in all 8 locales to drop
the "no cert import" promise. Remove `toast.tailscaleNotAvailable` key.
Docs:
- Wiki `features/virtual-printer.md`: rewrite the entire Tailscale section
— remove the LE-cert + HTTPS-Certs-toggle + tailscale-cert-operator
steps, document the toggle as informational, keep the Docker socket
mount + LXC TUN troubleshooting (those still apply for daemon
reachability).
- README: drop "the Tailscale benefit here is the tunnel, not cert-import
elimination" framing in favour of "surfaces the IP for paste into
slicer; CA import unchanged because BBL CA store, not system trust
store, is what gets validated".
Tests:
- `test_tailscale.py`: reduced to surviving `get_status` cases (binary
missing, command fails, success, empty DNSName, malformed JSON).
- `test_virtual_printer.py::test_sync_from_db_restarts_on_tailscale_disabled_change`
→ `test_sync_from_db_does_not_restart_on_tailscale_toggle` (toggle is
informational; `remove_instance` must NOT be called).
- `test_virtual_printer_api.py::TestVirtualPrinterTailscaleGuardAPI` →
`TestVirtualPrinterTailscaleToggleAPI` (single test asserts both
directions succeed and daemon is never consulted).
- `VirtualPrinterCard.test.tsx`: mock now stubs `getTailscaleStatus`;
FQDN-copy block drives data through that query.
DB column `tailscale_disabled` is kept (persists toggle state) — Postgres-
safe column drop is harder; future cleanup can remove if the toggle goes
away entirely. LE cert files on disk (`virtual_printer_ts.{crt,key}`) are
left in place per VP — harmless residue, manual cleanup if desired.
Verified: ruff clean, 2484 backend unit tests pass, 17 frontend VP-card
tests pass, frontend build succeeds, live service restart confirms VPs
serve `issuer=CN=Virtual Printer CA` on the Tailscale interface — slicer
trusts the user-imported bambuddy CA and skips hostname checks, so MQTT
connection succeeds end-to-end.
|
||
|
|
e8f252d2b8 | Post work PR #701 | ||
|
|
b3c37072b2 |
Add auto-dispatch toggle for virtual printer queue mode (#587)
Virtual printers in Queue mode now have an "Auto-dispatch" setting. When enabled (default), prints start automatically — preserving current behavior. When disabled, prints are added with manual_start so they wait for manual dispatch from the queue UI. |
||
|
|
6fd11ddc77 |
Add virtual printer Queue mode and sidebar badge indicators
Virtual Printer Changes: - Add new "Queue" mode that archives files and adds them to print queue - Rename modes: "Immediate" → "Archive", "Queue for Review" → "Review" - Three modes now: Archive (immediate), Review (pending), Queue (print queue) - Queue mode creates unassigned queue items (printer_id=null) Print Queue Changes: - Make printer_id nullable in PrintQueueItem model/schema - Add support for unassigned queue items (no printer assigned) - Queue page shows "Unassigned" filter option - Edit modal allows assigning printer to unassigned items - Unassigned items highlighted in orange Sidebar Badge Indicators: - Queue icon shows yellow badge with pending queue item count - Archive icon shows blue badge with pending upload count - Badges auto-update every 5 seconds and on window focus Other: - Update backup/restore to handle nullable printer_id and ams_mapping - Add database migration for nullable printer_id column - Update VirtualPrinterSettings tests for new modes - Update virtual printer API integration tests - Remove speed controls documentation from wiki (not implemented) |
||
|
|
88e84ca45a |
- Add Virtual Printer feature - emulate Bambu printer on network
- Virtual printer appears in Bambu Studio/Orca Slicer via SSDP discovery - Secure TLS/MQTT communication with auto-generated certificates - Queue mode (pending uploads) or auto-start mode - Configurable access code for authentication - Docker support with network_mode: host and certificate persistence - Fix backup/restore for virtual printer settings (auto-save no longer overwrites) |