mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 03:01:21 +02:00
1de161cb284085cf2ba2fd2a2910029a25e26b21
4227
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1de161cb28 | Updated BACKERS and SPONSORS | ||
|
|
c16506af32 |
chore(deps): bump vitest to 4.1.11 for the mocker path-traversal advisory
@vitest/mocker registers a redirect mock's target without checking it against Vite's file-serving allowlist, and the load hook then returns readFile(mock.redirect) as the module source. The target is built as join(root, new URL(redirect).pathname), which confines nothing -- a non-special scheme keeps ".." in pathname, so the join resolves outside the project root. GHSA-82fw-gwwq-j7x9, CVSS 5.9, CWE-22. Not reachable here. The unauthenticated path is the public mockerPlugin and interceptorPlugin exports, which attach to Vite's unauthenticated HMR socket for the benefit of third-party dev servers; nothing under frontend/src imports either. Browser mode, which registers over a token-authenticated RPC, is not installed -- @vitest/browser is an unmet optional peer -- and vitest.config.ts runs plain jsdom, so no dev server listens during a test run. Both packages are devDependencies and reach no shipped artifact. vitest and the eight @vitest/* packages go 4.1.8 -> 4.1.11, carrying es-module-lexer, expect-type, obug, std-env, tinyexec and tinyrainbow. Fifteen lockfile entries, all dev-scoped, none added or removed. The ^4.1.8 range already admitted the fix, but the declared floor is raised so a regenerated lockfile cannot resolve back beneath it. No src change, so the bundle is byte-identical and static/ does not move. |
||
|
|
9b2c49d866 | Updated BACKERS | ||
|
|
ec353a4f8c |
(security) Bumped fflate to 0.8.3 for a denial-of-service advisory reachable through three's compressed-format loaders (GHSA-px8p-9vwx-vf98, #3034)
|
||
|
|
4d12196a26 | Merge pull request #3034 from maziggy/dependabot/npm_and_yarn/frontend/npm_and_yarn-c46cce24cb | ||
|
|
7968886d5c |
Bump fflate in /frontend in the npm_and_yarn group across 1 directory
Bumps the npm_and_yarn group with 1 update in the /frontend directory: [fflate](https://github.com/101arrowz/fflate). Updates `fflate` from 0.8.2 to 0.8.3 - [Release notes](https://github.com/101arrowz/fflate/releases) - [Changelog](https://github.com/101arrowz/fflate/blob/master/CHANGELOG.md) - [Commits](https://github.com/101arrowz/fflate/compare/v0.8.2...v0.8.3) --- updated-dependencies: - dependency-name: fflate dependency-version: 0.8.3 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> |
||
|
|
2d16ed9ad0 | deps(frontend): bump browserslist and @humanfs/node for dev-scope advisories | ||
|
|
9321591158 |
deps(frontend): move the Tiptap stack to 3.31.1
GHSA-cp6q-959q-f8rh: @tiptap/core's mergeAttributes() copies keys out of Object.entries() with plain bracket assignment, so an own __proto__ key from JSON hits the legacy prototype setter rather than writing a property. The result carries an attacker-controlled prototype while Object.keys() and own-property checks show nothing, and ProseMirror's DOMSerializer.renderSpec() enumerates attribute objects with for...in -- so inherited src and onerror land on a rendered <img> and execute. Medium, CVSS 4.0 6.4, fixed in 3.30.4. Not reachable here. The advisory needs an untrusted object arriving at mergeAttributes(), or a custom or dynamic extension that preserves the attribute object. Nothing under frontend/src calls mergeAttributes or defines an extension, and RichTextEditor builds a fixed schema from StarterKit plus six stock extensions whose HTMLAttributes are static literals. Content crosses as an HTML string rather than JSON, so no own __proto__ key reaches an attrs object at all -- the DOM parser only fills attributes the schema declares -- and every read-only render is sanitized. Lockfile only: package.json already declared ^3.11.1, so the patched line was inside the range and only the stale lock held 3.19.0. No overrides entry needed. @tiptap/pm has narrowed its dependency set, so prosemirror-markdown, prosemirror-menu, prosemirror-collab, prosemirror-schema-basic, prosemirror-trailing-node, markdown-it and linkify-it leave the tree -- 16 packages, none imported by this repo. That retires the reachability note carried for linkify-it in 1.2.5. eslint, build with the Safari 16 baseline check, i18n parity and 3514 frontend tests across 256 files all pass. npm audit --omit=dev, which is what CI gates on, reports zero vulnerabilities. |
||
|
|
1e29518991 |
ci: balance the backend test shards by measured time, not test count
Backend Tests (shard 1/4) timed out after 10 minutes on
|
||
|
|
e8b901f54a | Updated BACKERS | ||
|
|
c331a3aedf | Bumped version v1.2.5.5 | ||
|
|
1b73fd29a8 |
Keep the RTSPS proxy's handler set off the server object (issue #3001)
asyncio's Server has a __dict__ and uvloop's, a Cython cdef class, does not, so the attribute added in 1.2.5.4 raised AttributeError under uvloop. Every RTSP camera failed before opening a socket, which is the diagnostic's capture_exception at 0 ms. Our own unit files all pin --loop asyncio for #1896 and were never affected. The reports come from units we do not write: the Proxmox VE Helper-Scripts LXC pins no loop, and installs predating that fix never gained the flag because update.sh does not rewrite unit files. The loop is not ours to assume, so fix the code rather than add another flag. The set moves to a module-level WeakKeyDictionary, keyed weakly so an abandoned proxy retires its own entry rather than leaking one and later handing a new server a dead one's handlers. Pinned on a real uvloop loop and, for hosts without uvloop, against a __slots__ server; conftest builds its loop from the default policy, so nothing in the suite had ever run the branch that broke. Also routes the two external-camera teardowns through close_tls_proxy, which #2968 introduced and left them out of. ----- Say so at startup when running on uvloop (issue #3001) An install on the wrong loop had no way to find out it was. #3001 was loud enough to notice; the #1896 upload truncation it is also exposed to is silent, and shows up as a print failing from a file that was corrupt on arrival. One WARNING in the lifespan naming the loop, the risk and the flag to add. A warning and not a refusal: uvicorn has already chosen its loop by the time any application code runs, and a server that answers requests beats one that will not boot. Asks the running loop what it is rather than whether uvloop imports -- uvicorn[standard] installs uvloop everywhere, so its presence says nothing -- and matches on the module name so the question never imports uvloop on a host without it. ----- Repair a service file written before the --loop asyncio pin (issue #3001) install.sh has pinned the loop since #1896, but nothing has ever rewritten an existing service file, so every native install created between 2025-11-28 (when uvicorn[standard] brought uvloop into the venv) and 2026-07-05 still runs on uvloop no matter how often it is updated. Both update scripts now add the flag themselves while the service is stopped, so it takes effect on the same restart -- systemd via sed, launchd via PlistBuddy, each backing the file up first and inserting nothing but the flag. Refuses to edit and explains instead when the shape is not a plain single-line uvicorn unit: a wrapper script, a continued ExecStart, several of them, a read-only file, or a service with drop-ins, since a drop-in may be what defines ExecStart and editing the fragment would change nothing while reporting success. A deliberate --loop uvloop is left alone. Reads the effective ExecStart from systemd rather than the file, so it is idempotent. |
||
|
|
0dfcff5925 |
Keep the RTSPS proxy's handler set off the server object (issue #3001)
asyncio's Server has a __dict__ and uvloop's, a Cython cdef class, does not, so the attribute added in 1.2.5.4 raised AttributeError under uvloop. Every RTSP camera failed before opening a socket, which is the diagnostic's capture_exception at 0 ms. Our own unit files all pin --loop asyncio for #1896 and were never affected. The reports come from units we do not write: the Proxmox VE Helper-Scripts LXC pins no loop, and installs predating that fix never gained the flag because update.sh does not rewrite unit files. The loop is not ours to assume, so fix the code rather than add another flag. The set moves to a module-level WeakKeyDictionary, keyed weakly so an abandoned proxy retires its own entry rather than leaking one and later handing a new server a dead one's handlers. Pinned on a real uvloop loop and, for hosts without uvloop, against a __slots__ server; conftest builds its loop from the default policy, so nothing in the suite had ever run the branch that broke. Also routes the two external-camera teardowns through close_tls_proxy, which #2968 introduced and left them out of. ----- Say so at startup when running on uvloop (issue #3001) An install on the wrong loop had no way to find out it was. #3001 was loud enough to notice; the #1896 upload truncation it is also exposed to is silent, and shows up as a print failing from a file that was corrupt on arrival. One WARNING in the lifespan naming the loop, the risk and the flag to add. A warning and not a refusal: uvicorn has already chosen its loop by the time any application code runs, and a server that answers requests beats one that will not boot. Asks the running loop what it is rather than whether uvloop imports -- uvicorn[standard] installs uvloop everywhere, so its presence says nothing -- and matches on the module name so the question never imports uvloop on a host without it. ----- Repair a service file written before the --loop asyncio pin (issue #3001) install.sh has pinned the loop since #1896, but nothing has ever rewritten an existing service file, so every native install created between 2025-11-28 (when uvicorn[standard] brought uvloop into the venv) and 2026-07-05 still runs on uvloop no matter how often it is updated. Both update scripts now add the flag themselves while the service is stopped, so it takes effect on the same restart -- systemd via sed, launchd via PlistBuddy, each backing the file up first and inserting nothing but the flag. Refuses to edit and explains instead when the shape is not a plain single-line uvicorn unit: a wrapper script, a continued ExecStart, several of them, a read-only file, or a service with drop-ins, since a drop-in may be what defines ExecStart and editing the fragment would change nothing while reporting success. A deliberate --loop uvloop is left alone. Reads the effective ExecStart from systemd rather than the file, so it is idempotent. |
||
|
|
3f1ed85791 | Housekeeping | ||
|
|
736e0b0f62 |
Merge pull request #3000 from maziggy/1.2.5.4
**Bambuddy 1.2.5.4** **What this is** A fix-heavy release on top of 1.2.5.3, with one substantial feature running through it: a spool now carries a different filament preset per printer model and a K profile per hotend, and every path that configures an AMS slot honours both. Around that sit scheduled AMS drying, Filament Track Switch support, Dutch as the fourteenth interface language, Home Assistant sensors bound to storage locations, and roughly 90 fixes - the heaviest runs on AMS and K profiles, on archives from prints Bambuddy did not dispatch, and on Spoolman cost attribution. Seven of the changes come from outside contributors. No breaking changes. Three new tables, one new column and six one-shot repair passes are applied automatically on both SQLite and PostgreSQL. If you are coming from 1.2.5 or earlier, read the 1.2.5 release notes first - all of its upgrade callouts apply to you as well. **Docker** docker compose pull docker compose up -d **Native install - recommended path** sudo BRANCH=main /opt/bambuddy/install/update.sh **Native install - manual path** sudo systemctl stop bambuddy cd /opt/bambuddy sudo -u bambuddy git fetch --prune --tags --force origin sudo -u bambuddy git checkout main sudo -u bambuddy git reset --hard origin/main sudo /opt/bambuddy/venv/bin/pip install -r requirements.txt cd frontend && sudo npm i sudo systemctl start bambuddy **Windows install** Download bambuddy-1.2.5.4-windows-x64-setup.exe from this release page (or the unversioned bambuddy-windows-x64-setup.exe alias). Existing Windows installs upgrade in place via the in-app Install Update flow. **New** - A spool carries a filament preset per printer model, and a K profile per hotend - A slicer preset is bound to a printer model: `Bambu PLA Basic @BBL X1C` is not the same preset as `@BBL H2C`. A spool stored exactly one, which was right until you used that spool on a second machine - the AMS slot on the other printer was then configured with a preset it has no profile for. The spool form's PA Profile tab becomes a **Printers** tab holding both halves: a model list on the left, and on the right that model's presets and the K profiles for each of its hotends. K profiles also distinguish High Flow from Standard nozzles, because a printer files each calibration under a nozzle id like `HH00-0.4` or `HS00-0.4` and can hold both for one diameter - a maintainer's H2D carries 102 high-flow entries and 6 standard, and the same filament reads a different K through each. A stored profile is no longer applied when the fitted nozzle disagrees; the picker marks it rather than letting it look configured while quietly doing nothing. Every path that configures a slot respects both: manual assign in either inventory mode, RFID auto-assign, the Spoolman tag link, the re-fire when a slot goes from empty to loaded, the re-apply after a calibration-table refresh, and the re-selection when a Filament Track Switch moves an AMS to the other nozzle. - Filament Track Switch: the inlet each AMS feeds, and K profiles that follow it - With a switch fitted an AMS is not wired to a nozzle. It is plumbed into one of the swi tch's two inlets and reaches both hotends through it, so every unit reports its extruder as "not fixed" and `ams_extruder_map` comes back empty. Bambuddy had nothing to fall back on but the AMS unit number, so AMS-A was badged R and AMS-B was badged L purely because their ids are 0 and 1, a third unit got no badge at all, and every one of those labels was wrong. The inlet is now read from the switch, and a print asks which nozzle to feed rather than guessing. - Scheduled AMS drying (#2703) - A drying run can start now, after a delay, or at a chosen time, and the printer's queue is held while it runs. The delayed dispatch goes through the same preflight the immediate button does, so a run the button would refuse is never silently published by the schedule; the blocking firmware reason is chosen by one shared rule, so a blocked AMS no longer reads two different ways depending on which control you pressed. A completed or cancelled run releases the printer - without that, a nightly off-peak dry with no printing in between worked on night one and silently did not on every night after. Pending and failed runs are shown on the printer card. - Dutch (nl) is now a supported interface language (#2891, requested and contributed by @Igiegel) - the fourteenth locale, listed as "Nederlands" in the language picker. - Home Assistant sensors can be bound to a storage location, so a drybox reports its own humidity (#2824) - the sensor readings you already surface on a printer card can now be attached to a shelf, drawer or drybox instead. - An Avery sheet can start at the first unused position (#2879, requested and contributed by @whitigol in #2918) - label PDFs always began in the top-left slot, so printing two labels onto a part-used 30-slot sheet meant spending the whole sheet or nothing. A **Starting label position** field says which slot to begin at, counted the way the sheet reads. - A fault's description is in the status response (#2926, proposed and analysed by @sadontsev) - `HMS_ERROR_DESCRIPTIONS` has been in the backend all along and the status response never carried it, so every consumer that wanted to tell a user *why* a print halted kept its own copy of the same 853 codes: this repo's Python table, the frontend modal's, and at least one third-party iOS client whose catalogue exists purely because the server would not say. - A virtual printer can be told which address to advertise (#2930, reported and diagnosed by @sebimarkgraf) - `VIRTUAL_PRINTER_ADVERTISE_ADDRESS` sets the address written into the MQTT status that slicers read their FTP upload destination from. On Docker bridge networking the virtual printer is reached on the host's LAN IP but binds something like `172.24.0.2`, and that private address was what the slicer got. - Printer file downloads can be selected in ranges, and print-history videos downloaded (#2850, requested and contributed by @logikal in #2853) - large selections are prepared on the app data volume rather than buffered in server and browser memory, with per-file compression, progress, cancellation and partial results. - The K value is on the AMS slot itself, not only in the popover (#2532, requested and contributed by @gyrene2083) - checking whether a calibration took across four slots was four hovers, and comparing two of them side by side was not possible at all. - Bambuddy asks a printer that refuses FTPS what it actually said (#2780, measured by @grolmus) - Python reports `[SSL: WRONG_VERSION_NUMBER]` and the bytes that caused it are gone, consumed by the TLS layer. The client now opens one plain connection straight after and logs the printer's own words, so a refusal like `421 Too many connections` identifies the fault outright. **Changed** - The spool form is wider, and colour, weight and cost move to their own **Color & Cost** tab. - Home Assistant sensors get their own settings tab instead of sharing the Smart Plugs one (#2824), and storage locations sort the way they are named rather than putting "Drybox 10" between "Drybox 1" and "Drybox 2". - Camera view mode is picked at the camera button, per printer, instead of one global dropdown in Settings. - A file dropped on a busy or offline printer is queued instead of refused (#2849, reporter @abraha2d). - An archive that arrives with only a name now says what to do about it (#2843, reporter @gyrene2083). - Generated thumbnails are lit, so one model no longer looks like the next (#2816, requested by @NaegeliJ, contributed by @sadontsev in #2861). - The Watchtower we recommend for daily builds is the maintained fork (#2917, reported by @CamelT0E). - The Windows installer build is split in two so a signing request can wait for a human (SignPath Foundation). **Fixes** **AMS slots, K profiles and the Filament Track Switch:** - A K profile could be saved against the wrong hotend and applied to the wrong one; RFID auto-assign picked one without checking which hotend it was calibrated on; and moving an AMS to the other nozzle re-selected for the wrong one. - Swapping a spool left the previous spool's preset name on the AMS slot card - the stored preset is fetched over REST while the tray arrives on the socket, and the card trusted the cached row over live telemetry. - The K value on a slot card went blank after a while and came back only after a backend restart (#2854); a slot could show the other nozzle's K value; a nozzle swapped out could keep showing its own; and a fresh install showed no K values at all until someone opened the Profiles page. - Reading a printer's calibration table could stall for 20 seconds. - Configure Slot could bind the default K value for a profile the picker was visibly showing. - Load and Unload in the AMS slot menu did nothing on a printer with a Filament Track Switch. - A manual K-profile calibration left a print in your archive - the automatic run was already filtered, the hand-started one was not, in either of its two shapes. - Automatic flow-dynamics calibration left an archive and two notifications behind. - An AMS slot could name the wrong white - `#FFFFFF` is Jade White in PLA Basic, Ivory White in PLA Matte and plain White in six other materials (#2875). - One ASA spool parked in the AMS added 20 minutes to every PLA print (#2886, reported by @FirstRulez). - Linking a Spoolman spool by tag configured the slot as generic filament. **Spools, inventory and Spoolman:** - A spool you assigned to an AMS slot unassigned itself seconds later, and the slot's colour changed with it (#2987, reported by @frethop). - Spoolman reset your renamed extra fields on every restart (#2983, reported by @ngreatorex). - Turning Spoolman mode on deleted every built-in slot assignment, and turning it back off did not restore them (#2812). - The first AMS sync after enabling Spoolman from Settings failed on every slot (#2903, diagnosed by @ojimpo), and the connection status described Bambuddy's memory rather than Spoolman. - Every Bambu RFID spool was added with the wrong empty-spool weight (#2909, reported and fixed by @ojimpo in #2923); existing rows are corrected on upgrade. - A clear spool synced to Spoolman as pure black (#2912, reported and contributed by @ojimpo in #2924), and translucent spools showed as an empty circle or solid black in four more places. - An AMS slot card showed a multi-colour spool as one flat band (#2967, reported by @NeighborGeek), and a wood, silk or gradient roll the AMS added for you was drawn as a flat disc. - An AMS slot assigned a PLA+ spool became unusable for PLA (#2902, reported by @doncaruana), and a wood-filled spool was named as plain PLA on its slot. - AMS slots were offered as places to store a spool. - The print dialog named an AMS slot after the wrong spool. **Cost and charging:** - Print cost ignored the linked Spoolman spool's price and always used the default rate (#2591, reported by @khaosdoctor), and a rescan or recalculation quietly replaced a Spoolman-derived cost with a default-rate one. - A print sent from Bambu Studio charged the wrong Spoolman spool and rewrote the archive to match (#2953, reported by @bitelvl1). - A print with no 3MF borrowed another model's filament and cost (#2843, reporter @gyrene2083), and one that could not debit a spool said nothing about it (#2812). Such a print can now be given its filament weight by hand (#1820, reported by @ojimpo). **Archives, uploads and printer connection:** - A print that could not fetch its own 3MF could be charged another plate's filament (#2957, reported by @doncaruana) - a same-named 3MF from the library or an earlier archive was accepted on filename alone, and Bambu Studio writes the printer-side name from the project title, so every plate of a project arrives under one name. Alongside it: a slow-but-healthy download was cut off at 30 seconds, two heavy FTPS transfers could run against one printer at once, and the cover thumbnail re-downloaded a 3MF another part of Bambuddy had just fetched. - A print archived without its 3MF never got its timelapse, and on a short print could be given somebody else's (#2957 follow-up, reported by @doncaruana). - A print started from the printer's own screen swept every FTP path, archived blank, and then blamed a slicer setting (#1820). - A print that started during an FTPS pause was archived empty forever, even after Bambuddy downloaded the file (#2957). - Some archived 3MFs lost their G-code when re-imported into the File Manager (#2993) - nothing was lost from the file; the Archives card judged it by what it holds while the library judged it by its filename, so a sliced 3MF stored as `Foo.3mf` came back as a source-only project with no Print button. Both now read the zip, and files already in your library are re-checked once on the next start. - Every print archived from a Bambu slice had no bed temperature, so preheat guessed one (#2989, reported by @senguendk); existing archives are repaired on upgrade. - A reprint of a file already on the printer lost its thumbnail (#2780 regression), and a printer that keeps its files on the card was written off as internal-storage-only (#2856, reporter @aishlai). - A failed upload told you to check the SD card, whatever had actually gone wrong (#2899, reported by @grolmus), and one handshake failure took out three queued jobs and every retry they had (#2898). - The connection diagnostic mistook a slicer's print for one of Bambuddy's own (#2843 follow-up). - Archive metadata could describe a plate that was never printed, and the archives API never reported which plate was printed (#2796, contributed by @sgiffhorn). - One unexplained disconnect could stop a printer reporting slicer print mappings for good. **Slicing:** - Everything the internal slicer produced was Bambu green, whatever filament was picked (#2977, reported by @fadudba), and a preset the slicer could not resolve was sliced as PLA at 200 C without saying so. - The internal slicer picked PETG for a PLA plate and an A1 process for a P1S (#2982, reported by @Igiegel); every slice that didn't name its own process quietly got the slowest one the slicer ships; and an H2D Pro classified every bundled preset as another printer's. - The slice dialog took settings from the file with "Use the file's built-in settings" switched off (#2942, reported by @zevulos), and slicing could ignore the process preset you picked. **Camera and timelapse:** - A camera snapshot occasionally logged an asyncio ERROR with a traceback into the camera code (#2968, reported by @ceasley). - Deleting a print with no 3MF left its timelapse and its uploaded source on disk (#2968). - Every ffmpeg failure logged its build banner instead of the error (#2968). **Queue, projects and batches:** - A batch order whose queued runs were deleted became a card that could neither be queued nor closed (#2960). - A queued job switched on printers that could never have printed it (#2876). - Archived projects crowded out the live ones in every project picker (#2888, reported by @e77). - Skip Objects went dead for the rest of a print if Bambuddy restarted while it was running. - The build plate of a powered-down printer can be cleared again (#2864, reported by @bryanmahin). **Interface:** - Every page was a blank white screen on iOS 16.0-16.3 (#2971, reported by @zevulos). - Card and row actions were unreachable on phones and tablets (#2865, reported by @aishlai). - The full-page G-code preview grew without limit and never drew anything (#2887, reported by @ojimpo). - Picking a spool near the bottom of the label dialog could scroll the dialog itself out of view, and a fully transparent spool printed a label with no QR code (#2918, found and fixed by @whitigol). - The File Manager's card menu no longer loses its top entry (#2846). - Closing the bug-report panel no longer throws the capture away and leaves the logs running (#2847). - A colour mismatch was reported between two filaments the app itself called "Blue" (#2941). - A printer card said "Unknown stage (72)" where it now says "Preparing". **Notifications, sensors and energy:** - Every notification provider vanished from the list after the inventory toggles were wired up (#2827), and two inventory toggles could never be turned on, so stock alerts have never been able to fire. - One Home Assistant sensor reporting a long text state could stop every printer sensor from updating. - The AMS temperature alarm fired hourly on ambient room heat, and silencing it cost the colour band (#2905, reported and contributed by @ojimpo in #2943); it also fired for the whole of a drying cycle (#1802, reporter @apizz). - Energy tracking stopped as soon as a second plug was linked to a printer (#2859). - A printer whose failure detection was not working showed a green "Safe" badge (#2952). **Platform and database:** - Bambuddy could not start against a PostgreSQL server whose messages are not in English (#2949, reported and diagnosed by @dvb6666). - Timestamps hours ahead of themselves on PostgreSQL in a non-UTC zone (#2855, reporter @Tolga-Unal). - A failure reason the backend derived and the same reason a user picked counted as two different reasons (#2974, reported by @ojimpo); existing rows are converted to one vocabulary. - A print that failed on an `hms[]` fault recorded an unlookupable error code. - Statistics forgot the name of a printer that was deleted with its history kept (#2873, reported by @rembomy). - The bundled chamber-preheat table was unreadable to the code that reads it. --- **Sponsors** Bambuddy is sustainable thanks to people who put their money where their use is. If this release saved you time or kept your farm running, the project runs on recurring contributions - there's no paid tier, no telemetry, no upsell, just sustainable maintenance. - GitHub Sponsors (recurring, 5 tiers from $5/mo to $300/mo) - https://github.com/sponsors/maziggy - Ko-fi (one-time or recurring) - https://ko-fi.com/maziggyv1.2.5.4 |
||
|
|
49c948d01a |
Pin the TLS floor on the cleartext-probe test's context
CodeQL reports the context as allowing TLS 1.0 and 1.1, and it is right about the mechanism: create_default_context() leaves minimum_version at MINIMUM_SUPPORTED, which is the build's floor rather than a guarantee. That is the reason every context in backend/app pins it, the reason bambu_ftp.py carries a comment saying so, and the reason this same file already pins it for the TLS-1.3 case further down. Line 127 was the one that did not. The floor cannot change what the test measures. The fixture answers with a plain FTP banner and speaks no TLS, so the handshake still fails as WRONG_VERSION_NUMBER, which is the assertion this test exists to make. |
||
|
|
9d35a4e8c6 |
Mark four test-side bandit false positives
The PR gate reported four new alerts, all in test code. A fixture's /tmp/x.3mf is a column value the migration under test UPDATEs, not a path anything opens. Two f-string statements interpolate column names from a literal list declared two lines above, with the id bound - a column name cannot be a bind parameter, which is why it is written into the string at all. The two joins move onto their own lines because a trailing marker would have taken line 64 past the 120-character limit. The fourth is a near miss rather than a finding: the line below it already carries the marker, as do four other wildcard sites in the same file. The wildcard is what that test exists to assert about. |
||
|
|
f918b4b1f3 | Merge remote-tracking branch 'origin/main' into 1.2.5.4 | ||
|
|
c5f552921e | Updated CHANGELOG | ||
|
|
909b9cb135 | Updated CHANGELOG | ||
|
|
0eb8d4b22f |
Mark the failure-reason migration's table name for bandit too
The line carried a noqa for ruff's S608 but nothing bandit reads, so the same rule was silent in one tool and reported as a medium SQL-injection finding in the other. Nothing is interpolated but `table`, which the loop takes from a literal tuple on the next line; the key and the label list are both bound parameters. A table name cannot be one, which is why it is written into the string at all. |
||
|
|
9755e08077 |
Decide whether a 3MF is sliced by looking inside it (issue #2993)
An archive that showed the green GCODE badge could re-import into the
File Manager as a source-only project with no Print button, seemingly at
random.
Nothing was ever lost from the file. The download serves the stored
bytes verbatim and the G-code was still in the zip; the two sides simply
asked different questions. Archives looked inside the file. The library
looked at the filename. So a sliced 3MF stored as Foo.3mf rather than
Foo.gcode.3mf earned the badge and lost the Print button, and which one
you got depended on how the print had reached the printer -- a slicer's
LAN send names it .gcode.3mf, a per-plate export or a cloud-dispatched
print does not.
Both sides now ask one shared predicate about the zip itself, and every
route into the library classifies on content. Only the central directory
is read, and only when the name has not already settled it, so ingest
costs nothing extra -- the external scan opens each 3MF for its
thumbnail regardless. Rows already stored are re-checked once, internal
ones only: an external row points at a mount that may be slow or absent,
and startup is the worst place to discover that.
The Slice action moves with it. Its refusal to slice an output was as
name-bound as the Print gate, and without that a file that correctly
gained a Print button would have offered to re-slice its own G-code.
|
||
|
|
f616a6bca9 |
Show the spool that is in the AMS slot, not the one that was
Pull a Bambu ABS Orange out of A1, put a PLA Matte Dark Blue in, and the
slot card still read "Bambu ABS" against the new colour until the page
was reloaded.
Three things stood between the swap and a correct card.
The RFID auto-assign rewrites the slot's slot_preset_mappings row and
then broadcast an event that refreshed everything except the query that
reads it. Only the manual assign path invalidated that one.
Those queries then sat behind the 3s cascade debounce, which exists for
print completion, where one event fans out across half the app. A swap
touches one slot and the user is standing at the printer looking at the
card; worse, the timer restarts on every further event, so a busy moment
could defer it indefinitely. Slot changes now invalidate immediately.
And the card trusted the stored preset over live telemetry outright.
That priority is why a hand-picked preset name stays on a slot, but it
also let a cached row outrank what the printer was reporting. The row is
now skipped when it names a different official Bambu filament than the
tray does, so the card is right from the status push alone. User and
local presets carry ids that genuinely cannot be compared and are left
exactly as they were.
Spoolman mode was the worse half of the same bug: its AMS sync writes
the same row but announced nothing at all, so there was no event to
refresh on. It now reports each slot it changed or cleared.
|
||
|
|
2b3fe10a64 |
Let sub-project groups be collapsed on the Projects page (issue #2991)
A project with sub-projects drew every one of them expanded, at every
level, with nothing to shut. Over a three-level hierarchy and a couple of
hundred archives that makes the page one long scroll.
Each group's caption is now a chevron that folds that group, and a
Collapse pill next to the status filter tabs sets the default for the
page and is remembered across reloads. A shut group takes one grid cell
rather than a full-width row, so folding a deep tree actually gets the
page back.
The count on the caption is of the cards nested there, not the parent
card's badge: the API counts sub-projects across every status on purpose,
so under the Active filter the badge can legitimately say 2 where one
card unfolds. A count that disagrees with what unfolds is worse than no
count at all.
|
||
|
|
049afea980 |
Refuse a same-named 3MF that contradicts the running print (issue #2957)
When a print's own 3MF cannot be fetched the usage tracker borrows one from the
library or a previous archive, matching on the filename stem. That is far weaker
evidence than it looks: Bambu Studio writes the printer-side filename from the
project's Title metadata, so every plate of a project reaches the printer under
one name however the file was renamed on disk.
The reporter's single-filament job was handed a previous archive's three-filament
plate. Three spools were debited for material that was never extruded, and
nothing on the archive said the numbers were someone else's.
A candidate is now rejected when it positively contradicts the print - a
different plate, or a filament count the slicer's ams_mapping disagrees with -
and the accepted one is logged with both expectations. Only on a contradiction:
the plate needs firmware that echoes it and the count needs a print command
Bambuddy saw, and refusing everything uncorroborated would retire the fallback
recovery this same issue asked for.
The count is scoped to one plate or not compared at all. Unscoped, the filament
reader collects every <filament> in the file, and that sum against one plate's
count would reject every multi-plate library upload on exactly the firmwares
that cannot tell us the plate.
-----
Give a download the time the file needs, and one printer at a time (issue #2957)
ftp_timeout is handed to every download as both the socket inactivity timeout
and the whole-transfer deadline, which makes its 30s default a cap on how big a
file a printer may serve. The reporter measured one 5.4 MB 3MF at 45s off a worn
P1S SD card and 25s off a new one, and a 15.15 MB 3MF at 105s. None of those
links were broken - they were slow, which is what the inactivity timeout exists
to tell apart.
download_to_file already asks for SIZE. It now reports it, and the total
deadline follows the file at the 25 KB/s floor _upload_deadline has used since
do, so #2572's cap on the executor queue wait is untouched. Capped at 300s for a
reason that is not about FTP: on_print_start holds a pooled DB connection across
its whole 3MF hunt.
Downloads also take turns per printer now. He watched Bambu Studio lose its own
connection while Bambuddy pulled a 12 MB 3MF, and a later log caught two
Bambuddy downloads of the same file overlapping at print start. The gate is
soft - whoever cannot have it within 30s goes anyway, because a print losing its
3MF to queueing is worse than the contention, and a soft gate cannot deadlock.
It is also meaningful for the first time. The 90s cap on a multi-path lookup
returned while its worker kept walking the remaining paths, still on the
printer's socket; that walk is now cancelled and waited out before the printer
is handed on.
-----
Look in the shared 3MF cache again before each cover retry (issue #2957)
The cover endpoint and the print-start archive flow share a cache so whichever
fetches the 3MF first hands it to the other (#972). The cover consulted it once
on the way in, then retried for up to two and a half minutes without looking
again.
In the reporter's log the archive flow published the file 42 seconds into that
sequence and the cover's third attempt still pulled its own 5,250,969-byte copy,
off a printer that was mid-print on the same SD card.
A file picked up that way is left alone rather than re-registered under this
endpoint's own name or deleted on the way out. It is the archive flow's.
|
||
|
|
0eafe312c6 |
Repair the bed temperature on archives written before the fix (issue #2989)
The forward fix reads the array the fitted plate points at, but only for
archives made after it. Everything already in the library stays blank, and
preheat keeps falling back to the keep-warm bed temperature whenever one of
those jobs is reprinted from the queue - 0 of 455 real 3MFs had resolved.
A one-shot pass re-reads the 3MF already on disk, gated by a settings flag the
way #2614's repair is: the rows it cannot fill are exactly the ones it would
reopen every boot. It fills NULLs only. Nothing recorded is overwritten, an
archive whose file is gone stays NULL, and a corrupted 3MF is skipped rather
than failing startup.
The plate mapping moves to threemf_tools.bed_temperature_from_config so the
ingest path and the repair cannot read a 3MF differently - the same drift
move.
_extract_settings_from_content is deleted. Nothing called it anywhere in the
repo, and it carried the old bed_temperature mapping this issue fixed.
|
||
|
|
20627b2188 |
Log ffmpeg's error instead of its build banner
ffmpeg opens every run with ~20 lines of version and build banner and prints
its diagnosis last, so the stderr[:200] eight of the nine call sites used kept
the banner and threw the error away. The reporter's twelve capture failures all
read "ffmpeg version 7.1.4 ... configuration: --prefix=/usr --extra-version=",
identical on every install; the exit code was the only usable byte.
The banner-stripping summariser written for #925 lived private to the camera
route. It now lives in backend/app/utils/ffmpeg_output.py and every ffmpeg and
ffprobe stderr goes through it. Two things the scattered copies also got wrong:
four logged the input URL unmasked, publishing a printer access code or camera
password, and four called a bare .decode() on bytes ffmpeg copies stream
fragments into.
-----
Delete the files a no-3MF archive owns, without taking a printer folder
Both delete paths derived the directory from file_path, which such an archive
does not have, so they removed nothing and logged it at ERROR under a SECURITY
banner. That was true when the archive was an empty row and stopped being true
once one could hold a timelapse and finish photos in <archive_dir>/<id>/ and an
uploaded source in archive/no_source/<id>/.
The two are cleaned up by different means, because <archive_dir>/<id> shares a
namespace with the per-printer folders: a normal archive lives at
<archive_dir>/<printer_id>/<timestamp>_<name>/, so archive/1 is printer 1's
folder and also the directory the shared helper hands archive id 1. Ids come
from unrelated sequences, so the first few archives collide with the printers
on every install, and an rmtree there takes every print that printer made --
measured on a scratch tree. no_source/<id> is a level deeper under a name no
printer id can take and is removed whole; the id-named directory gives up only
its photos subdirectory and the video the row records, then goes only if that
left it empty. The depth guard moves from one to two for the same reason: a
file_path that lost a path component could point the delete at a printer
folder, and no archive directory has been one level deep since the first
commit.
Hard delete had its own copy of these rules, which the helper's docstring says
it exists to prevent, and it had diverged -- it skipped the print-log thumbnail
cleanup whenever a guard tripped.
-----
Stop the RTSPS proxy leaving a handler behind at shutdown
asyncio.start_server keeps only a weak reference to the connection callback's
task, so a handler still awaiting its forwarders could be collected while
pending -- "Task was destroyed but it is pending!", at ERROR with a traceback
into camera.py, once every few hundred snapshots. Teardown had the matching
gap: server.close() leaves established connections running, so the close waited
on a handler that only finishes when the peer drops, and ffmpeg has already
been reaped by then.
Handlers are held for as long as they run and cancelled at shutdown, which is
Server.close_clients() by hand -- that landed in 3.13 and Bambuddy supports
3.10. Both the snapshot path and the streaming endpoint share the shutdown.
|
||
|
|
ff9b956156 |
Read the bed temperature from the plate the project is sliced for (issue #2989)
BambuStudio writes no bed_temperature key. It stores a per-filament array per
plate type and names the fitted plate in curr_bed_type; bed_temperature is the
Orca/Prusa spelling, so the lookup matched nothing and every archive from a
Bambu slice stored NULL - 0 of 455 real 3MFs resolved. Preheat then fell back
to the keep-warm bed temperature on jobs that had one all along.
Plate names and the plate-to-key mapping are BambuStudio's own get_bed_temp_key
and get_bed_temp_1st_layer_key. First-layer value preferred, highest entry in
the per-filament array taken, an all-zero array left unrecorded.
|
||
|
|
ffc55b6a6e |
Skip the manual K calibration line as an internal printer job (issue #2957 follow-up)
Manual flow dynamics has two shapes and each reports under its own name with
no auto_ prefix. pa_pattern_calib_mode was already filtered; pa_line_calib_mode
was not, so it still swept FTP for a 3MF that cannot exist and wrote a no-3MF
archive named after the calibration.
|
||
|
|
597b2b44f1 | Updated BACKERS | ||
|
|
2e405afcd1 |
Decide whether a 3MF is sliced by looking inside it (issue #2993)
An archive that showed the green GCODE badge could re-import into the File Manager as a source-only project with no Print button, seemingly at random. Nothing was ever lost from the file. The download serves the stored bytes verbatim and the G-code was still in the zip; the two sides simply asked different questions. Archives looked inside the file. The library looked at the filename. So a sliced 3MF stored as Foo.3mf rather than Foo.gcode.3mf earned the badge and lost the Print button, and which one you got depended on how the print had reached the printer -- a slicer's LAN send names it .gcode.3mf, a per-plate export or a cloud-dispatched print does not. Both sides now ask one shared predicate about the zip itself, and every route into the library classifies on content. Only the central directory is read, and only when the name has not already settled it, so ingest costs nothing extra -- the external scan opens each 3MF for its thumbnail regardless. Rows already stored are re-checked once, internal ones only: an external row points at a mount that may be slow or absent, and startup is the worst place to discover that. The Slice action moves with it. Its refusal to slice an output was as name-bound as the Print gate, and without that a file that correctly gained a Print button would have offered to re-slice its own G-code. |
||
|
|
7363d5fd33 |
Show the spool that is in the AMS slot, not the one that was
Pull a Bambu ABS Orange out of A1, put a PLA Matte Dark Blue in, and the slot card still read "Bambu ABS" against the new colour until the page was reloaded. Three things stood between the swap and a correct card. The RFID auto-assign rewrites the slot's slot_preset_mappings row and then broadcast an event that refreshed everything except the query that reads it. Only the manual assign path invalidated that one. Those queries then sat behind the 3s cascade debounce, which exists for print completion, where one event fans out across half the app. A swap touches one slot and the user is standing at the printer looking at the card; worse, the timer restarts on every further event, so a busy moment could defer it indefinitely. Slot changes now invalidate immediately. And the card trusted the stored preset over live telemetry outright. That priority is why a hand-picked preset name stays on a slot, but it also let a cached row outrank what the printer was reporting. The row is now skipped when it names a different official Bambu filament than the tray does, so the card is right from the status push alone. User and local presets carry ids that genuinely cannot be compared and are left exactly as they were. Spoolman mode was the worse half of the same bug: its AMS sync writes the same row but announced nothing at all, so there was no event to refresh on. It now reports each slot it changed or cleared. |
||
|
|
a70047c75d |
Let sub-project groups be collapsed on the Projects page (issue #2991)
A project with sub-projects drew every one of them expanded, at every level, with nothing to shut. Over a three-level hierarchy and a couple of hundred archives that makes the page one long scroll. Each group's caption is now a chevron that folds that group, and a Collapse pill next to the status filter tabs sets the default for the page and is remembered across reloads. A shut group takes one grid cell rather than a full-width row, so folding a deep tree actually gets the page back. The count on the caption is of the cards nested there, not the parent card's badge: the API counts sub-projects across every status on purpose, so under the Active filter the badge can legitimately say 2 where one card unfolds. A count that disagrees with what unfolds is worse than no count at all. |
||
|
|
7c10412f99 |
Refuse a same-named 3MF that contradicts the running print (issue #2957)
When a print's own 3MF cannot be fetched the usage tracker borrows one from the library or a previous archive, matching on the filename stem. That is far weaker evidence than it looks: Bambu Studio writes the printer-side filename from the project's Title metadata, so every plate of a project reaches the printer under one name however the file was renamed on disk. The reporter's single-filament job was handed a previous archive's three-filament plate. Three spools were debited for material that was never extruded, and nothing on the archive said the numbers were someone else's. A candidate is now rejected when it positively contradicts the print - a different plate, or a filament count the slicer's ams_mapping disagrees with - and the accepted one is logged with both expectations. Only on a contradiction: the plate needs firmware that echoes it and the count needs a print command Bambuddy saw, and refusing everything uncorroborated would retire the fallback recovery this same issue asked for. The count is scoped to one plate or not compared at all. Unscoped, the filament reader collects every <filament> in the file, and that sum against one plate's count would reject every multi-plate library upload on exactly the firmwares that cannot tell us the plate. ----- Give a download the time the file needs, and one printer at a time (issue #2957) ftp_timeout is handed to every download as both the socket inactivity timeout and the whole-transfer deadline, which makes its 30s default a cap on how big a file a printer may serve. The reporter measured one 5.4 MB 3MF at 45s off a worn P1S SD card and 25s off a new one, and a 15.15 MB 3MF at 105s. None of those links were broken - they were slow, which is what the inactivity timeout exists to tell apart. download_to_file already asks for SIZE. It now reports it, and the total deadline follows the file at the 25 KB/s floor _upload_deadline has used since do, so #2572's cap on the executor queue wait is untouched. Capped at 300s for a reason that is not about FTP: on_print_start holds a pooled DB connection across its whole 3MF hunt. Downloads also take turns per printer now. He watched Bambu Studio lose its own connection while Bambuddy pulled a 12 MB 3MF, and a later log caught two Bambuddy downloads of the same file overlapping at print start. The gate is soft - whoever cannot have it within 30s goes anyway, because a print losing its 3MF to queueing is worse than the contention, and a soft gate cannot deadlock. It is also meaningful for the first time. The 90s cap on a multi-path lookup returned while its worker kept walking the remaining paths, still on the printer's socket; that walk is now cancelled and waited out before the printer is handed on. ----- Look in the shared 3MF cache again before each cover retry (issue #2957) The cover endpoint and the print-start archive flow share a cache so whichever fetches the 3MF first hands it to the other (#972). The cover consulted it once on the way in, then retried for up to two and a half minutes without looking again. In the reporter's log the archive flow published the file 42 seconds into that sequence and the cover's third attempt still pulled its own 5,250,969-byte copy, off a printer that was mid-print on the same SD card. A file picked up that way is left alone rather than re-registered under this endpoint's own name or deleted on the way out. It is the archive flow's. |
||
|
|
b0ecb8fd88 |
Repair the bed temperature on archives written before the fix (issue #2989)
The forward fix reads the array the fitted plate points at, but only for archives made after it. Everything already in the library stays blank, and preheat keeps falling back to the keep-warm bed temperature whenever one of those jobs is reprinted from the queue - 0 of 455 real 3MFs had resolved. A one-shot pass re-reads the 3MF already on disk, gated by a settings flag the way #2614's repair is: the rows it cannot fill are exactly the ones it would reopen every boot. It fills NULLs only. Nothing recorded is overwritten, an archive whose file is gone stays NULL, and a corrupted 3MF is skipped rather than failing startup. The plate mapping moves to threemf_tools.bed_temperature_from_config so the ingest path and the repair cannot read a 3MF differently - the same drift move. _extract_settings_from_content is deleted. Nothing called it anywhere in the repo, and it carried the old bed_temperature mapping this issue fixed. |
||
|
|
d4477e9b71 |
Log ffmpeg's error instead of its build banner
ffmpeg opens every run with ~20 lines of version and build banner and prints its diagnosis last, so the stderr[:200] eight of the nine call sites used kept the banner and threw the error away. The reporter's twelve capture failures all read "ffmpeg version 7.1.4 ... configuration: --prefix=/usr --extra-version=", identical on every install; the exit code was the only usable byte. The banner-stripping summariser written for #925 lived private to the camera route. It now lives in backend/app/utils/ffmpeg_output.py and every ffmpeg and ffprobe stderr goes through it. Two things the scattered copies also got wrong: four logged the input URL unmasked, publishing a printer access code or camera password, and four called a bare .decode() on bytes ffmpeg copies stream fragments into. ----- Delete the files a no-3MF archive owns, without taking a printer folder Both delete paths derived the directory from file_path, which such an archive does not have, so they removed nothing and logged it at ERROR under a SECURITY banner. That was true when the archive was an empty row and stopped being true once one could hold a timelapse and finish photos in <archive_dir>/<id>/ and an uploaded source in archive/no_source/<id>/. The two are cleaned up by different means, because <archive_dir>/<id> shares a namespace with the per-printer folders: a normal archive lives at <archive_dir>/<printer_id>/<timestamp>_<name>/, so archive/1 is printer 1's folder and also the directory the shared helper hands archive id 1. Ids come from unrelated sequences, so the first few archives collide with the printers on every install, and an rmtree there takes every print that printer made -- measured on a scratch tree. no_source/<id> is a level deeper under a name no printer id can take and is removed whole; the id-named directory gives up only its photos subdirectory and the video the row records, then goes only if that left it empty. The depth guard moves from one to two for the same reason: a file_path that lost a path component could point the delete at a printer folder, and no archive directory has been one level deep since the first commit. Hard delete had its own copy of these rules, which the helper's docstring says it exists to prevent, and it had diverged -- it skipped the print-log thumbnail cleanup whenever a guard tripped. ----- Stop the RTSPS proxy leaving a handler behind at shutdown asyncio.start_server keeps only a weak reference to the connection callback's task, so a handler still awaiting its forwarders could be collected while pending -- "Task was destroyed but it is pending!", at ERROR with a traceback into camera.py, once every few hundred snapshots. Teardown had the matching gap: server.close() leaves established connections running, so the close waited on a handler that only finishes when the peer drops, and ffmpeg has already been reaped by then. Handlers are held for as long as they run and cancelled at shutdown, which is Server.close_clients() by hand -- that landed in 3.13 and Bambuddy supports 3.10. Both the snapshot path and the streaming endpoint share the shutdown. |
||
|
|
c001f596bf |
Read the bed temperature from the plate the project is sliced for (issue #2989)
BambuStudio writes no bed_temperature key. It stores a per-filament array per plate type and names the fitted plate in curr_bed_type; bed_temperature is the Orca/Prusa spelling, so the lookup matched nothing and every archive from a Bambu slice stored NULL - 0 of 455 real 3MFs resolved. Preheat then fell back to the keep-warm bed temperature on jobs that had one all along. Plate names and the plate-to-key mapping are BambuStudio's own get_bed_temp_key and get_bed_temp_1st_layer_key. First-layer value preferred, highest entry in the per-filament array taken, an all-zero array left unrecorded. |
||
|
|
164382b38b |
Skip the manual K calibration line as an internal printer job (issue #2957 follow-up)
Manual flow dynamics has two shapes and each reports under its own name with no auto_ prefix. pa_pattern_calib_mode was already filtered; pa_line_calib_mode was not, so it still swept FTP for a 3MF that cannot exist and wrote a no-3MF archive named after the calibration. |
||
|
|
27ecdeb7b9 | Updated BACKERS | ||
|
|
880c7bfe79 | Updated BACKERS | ||
|
|
f1096c13eb | Updated CHANGELOG | ||
|
|
029c3ac4b7 | Updated CHANGELOG | ||
|
|
8cce10b60a |
Skip the manual K calibration the way the automatic one is skipped
Bambuddy already recognises the printer's automatic pressure-advance run,
auto_pa_line_calib_mode, and leaves no archive and sends no notification
for it. Started by hand rather than automatically before a print, the
same calibration reports under a different name: it prints a pattern
where the automatic one prints a line, and carries no auto_ prefix, so
pa_pattern_calib_mode matched nothing.
It arrives exactly the way the automatic one does -- a bare subtask name
with no /usr/ path -- so it hit the same outcome the module was written
to prevent: an FTP sweep for a 3MF that cannot exist, six candidate names
across five directories with retries, and then a no-3MF archive named
after the calibration, on a printer in the middle of calibrating.
One entry on INTERNAL_JOB_NAMES covers it. That set is the single place
both the print-start and print-complete callbacks consult, so archiving,
the 3MF sweep and the notifications are all handled by the one line.
Matching stays exact after normalising path, suffix and case. The
negative cases are extended alongside the positive ones, so a file
somebody deliberately named pa_pattern_calib_mode_v2.3mf is still
archived as the print it is.
The manual PA *line* method, if it reports its own name, is not covered
here -- the list is deliberately limited to names that have actually been
observed rather than ones that seem likely.
|
||
|
|
9b83e8eba4 |
Send AMS tray colours as uppercase hex (issue #2987)
Assigning a spool to an AMS slot unassigned it again seconds later, and
the slot's colour changed at the same time. It presented as Bambu Studio
and Bambuddy fighting over the slot. The reporter's log shows Bambuddy
losing to itself.
P1S firmware 01.10.00.00 reads every lowercase hex letter in an AMS
tray_color as a zero, and hides it completely: the command response
echoes back the value that was sent and reports result "success", so
only the next AMS push says what was really stored. The spool-assign
path sent spool.rgba verbatim and that column stores lowercase. From the
bundle:
sent 09ff00ff -> AMS reports 09000000
sent ff5100ff -> AMS reports 00510000
sent 090000FF -> AMS reports 090000FF
That is the visible colour change, and it is also what deleted the
assignment. The auto-unlink sweep asks whether the slot still matches
the spool assigned to it; the mangled colour no longer did, so the
assignment Bambuddy had made four seconds earlier was removed.
colors_similar('09000000', '09FF00FF') is False, which is the whole of
it.
Re-assigning could not recover, because the Configure Slot dialog seeds
its colour from whatever the printer currently reports. It wrote the
mangled colour back and cemented it, which is the loop the report
describes in its steps 4 and 5.
Colours are now uppercased where the command is assembled rather than in
each of the four routes that configure a slot. A caller that forgets is
exactly how this arrived. Nothing else changes: no padding, no invented
alpha, no six-to-eight widening, and tray_type and tray_sub_brands keep
their case, where it carries meaning -- "PLA Matte" is a product line,
"PLA MATTE" is not.
Two paths deliberately left alone. The developer-mode probe re-sends the
colour the printer itself just reported so that the probe is inert;
uppercasing there would turn it into a write. And the Virtual Printer
forwards the slicer's own command verbatim -- Studio could in principle
hit the same firmware bug, but nothing here evidences that it sends
lowercase, and rewriting a slicer payload inside a transparent proxy is
not a change to make on a hunch.
Two more defects from the same log.
A spool with a brand and no subtype was configured with the string
"None" in its name: the branded branch interpolated spool.subtype
without checking it while the unbranded branch guarded it, so
"Sunlu PLA Matte None" went on the wire and into Studio's display.
And the FTP log is readable again. A 426 whose bytes Bambuddy has
already verified against the printer is how Bambu FTPS normally ends a
transfer, not a fault, so it drops from WARNING to INFO. It fired 54
times in this one bundle, every one followed by a completed upload, and
it was burying the 26 TLS handshake failures in the same log that
actually cost the reporter two prints. A 426 whose bytes do not verify
is still an error and still fails the upload.
The handshake failures themselves are printer-side FTPS cool-off under
load and are not touched here.
|
||
|
|
450f9a6f20 |
Derive the chamber target from the trays the print loads (issue #2886)
Preheat took the maximum chamber target across every loaded AMS tray,
with no reference to the job. The reporter's P2S holds PETG Pro, PLA,
ASA and PETG; the ASA row of the filament map says 45C, so a PLA-only
plate was dispatched with chamber_target=45C, the bed driven to 90C to
reach it, and the full 900s max-wait plus 300s soak burned before the
upload started. Every time -- a P2S has no chamber heater and its
chamber tops out around 33C, so the wait can only ever end on the
timeout. Their log carries fifteen of these.
The intent was never in doubt. The resolution order documented one
screen above _derive_chamber_target reads "PLA-only print derives 0 ->
chamber phase auto-skips", but it was implemented as PLA-only AMS
rather than PLA-only print, and only misfires on a mixed load.
The derivation now reads the trays the item's ams_mapping names -- the
same array the print command puts on the wire, [-1, -1, -1, 1] in their
case, addressing exactly the PLA slot -- so the ASA two slots over
contributes nothing and the stage skips outright. Multi-material prints
are unaffected: the maximum is still taken, across the trays the plate
actually loads, so an ASA the print does use is still binding.
An item whose mapping is missing or still unresolved keeps the
whole-unit scan. That is the only signal left, and narrowing to nothing
would disable preheat for prints that need it -- the failure mode worth
avoiding here is the silent one.
The bed hold between jobs is gated on the same derivation and was
holding beds at 90C for the same wrong reason. It now reads the next
item's mapping too, where that item has one.
The external spool is no longer invisible to this. The scan only ever
looked at raw_data['ams'], so an ASA print fed from the external feed
derived 0 and got no preheat at all; a mapping naming 254/255 is now
honoured. An item with no mapping still derives from the AMS alone, so
nothing starts preheating that did not before.
Tray addressing matches _build_loaded_filaments, which is what produced
the ids in the mapping being read back: ams_id * 4 + tray_id, the bare
unit id for an AMS-HT from 128, and the firmware's own vt_tray id for
an external feed. Ids are coerced because this firmware reports them as
strings.
|
||
|
|
21fd476668 |
Ask Spoolman which extra fields it has, once (issue #2983)
Bambuddy checked whether one of its four custom spool fields existed with
GET /field/spool/{name}. Spoolman has never served that. Its API declares
only POST and DELETE at that path -- confirmed against the live server's
own OpenAPI document -- so the probe answered 405 Method Not Allowed
every time and the check could not succeed on any version.
Every call therefore fell through to POST /field/spool/{name}, and that
endpoint is an upsert rather than a create. It answers 200 whether or not
the field is already there, so a field the user had renamed, retyped or
given a default to in Spoolman's own UI was reset to Bambuddy's version
of it, and an untrue "Created Spoolman extra field" was logged beside it.
The reporter's log carried 60 of those lines over three days -- once per
field per client init, which is every restart and every settings save.
Existence now comes from GET /field/spool, the listing endpoint, matched
on each row's `key`. Matching on `key` rather than the display `name` is
the part that fixes the overwrite: a renamed field is the same field, and
reading it as a missing one is what re-created it. A field that already
exists is now left completely alone.
The listing is read once per client and banked, so registering all four
fields costs one request instead of four, and a client that has already
looked makes none at all. Only a successful read is banked -- a client
that could not reach the listing asks again for the next field it has not
seen, so one transient failure does not leave it posting blind, and
overwriting, for the rest of its life.
An unreadable listing still falls back to attempting the POST. Registration
is best-effort by contract: it must not turn a write that might still
succeed into one that never happens, so an unexpected Spoolman build is no
worse off than before.
Measured against Spoolman 0.23.1: a field renamed to "Bambu RFID Tag"
survives a full registration pass that previously reset it, the pass makes
one GET and one POST for the single genuinely-missing field where it used
to make four POSTs, and a second pass on the same client makes no requests
at all.
The fake in test_spoolman_extra_field_registration_2903 modelled the
per-field path as a working probe, which is the assumption this bug was
built on; it now answers 405 as the real server does, and its assertions
follow the listing. 18 new tests cover the rest, 10 of which fail against
the old code.
|
||
|
|
112e58e5b9 |
Match slicer presets on what they declare, not what they are named (issue #2982)
The internal slicer picked PETG for a PLA plate and an A1 process for a
P1S. Both come from the sidecar's bundled-profile listing, fixed in the
sidecar repo; this is the consuming half plus the hardening that keeps an
older sidecar degrading rather than breaking.
Standard-tier presets now carry the compatible_printers the sidecar
reports. That list is the only truthful account of which printer a preset
belongs to, because the bundle ships no process preset named after a P1S,
an X1, an X1E or an H2D Pro -- all ten of the P1S's are named "@BBL X1C"
and name the P1S only in that list. Reading the printer out of the preset
NAME therefore made a P1S look like it had no compatible process at all:
all 198 hid behind "Show all" and the auto-pick fell through to an
alphabetically-first 0.06mm Fine @BBL A1 0.2 nozzle the CLI refused. A
P1S now gets 0.20mm Standard @BBL X1C and 73 filaments instead of 4. An
older sidecar reports nothing here, which leaves the name matcher in
place -- degraded as before, not broken.
Material is now a hard partition in the filament pre-pick rather than a
+10 bonus. A preset stating a different material than the plate asks for
is the wrong preset, not a worse one: wrong nozzle temperature, wrong bed
temperature, wrong flow. A preset stating NO material stays eligible --
unknown is not wrong, and 32 shipped profiles genuinely have none. The
same rule reaches the retain path, which held a slot on
printer-compatibility alone and so cemented a wrong-material pick through
every re-pick. A preset the user chose themselves is exempt: printing
PETG on a plate a designer labelled PLA is a legitimate thing to do, and
this rule exists to correct the auto-pick, not to overrule the user.
Two more, both found while tracing this and neither reported:
Among process presets equally valid for the selected printer, the one
nearest a 0.2mm layer height now wins. Within a tier the list is
alphabetical and Bambu's naming puts the finest height first, so every
slice that did not name its own process silently got 0.08mm Extra Fine on
an X1 Carbon and 0.06mm Fine on an A1 mini -- correct presets, nobody's
default. Ties break toward the coarser, faster height; a name with no
readable height is still pickable when it is the only candidate; a
process the 3MF named still wins outright.
H2DP is aliased to H2D Pro, the same shape as the A1M rename in #1649 --
the bundle spells the model one way in preset names and another in the
printer preset, so an H2D Pro classified all 198 processes as another
printer's. Deliberately narrow: H2DP and a plain H2D are different
machines and must not collapse.
A dropdown the printer filter would empty now shows the unfiltered list
instead. That state was reachable for four printer models and told the
user nothing; a visible preset for the wrong printer can be changed, an
empty dropdown cannot.
Verified against live Orca 2.4.2 and BambuStudio 02.08.02.61 sidecars
over the real 1156- and 1792-profile trees: every one of the eight
printer models tested now auto-picks a 0.20mm process for its own
printer, a PLA plate draws a PLA preset and a PETG plate a PETG one.
Each change was confirmed to fail its tests when reverted.
|
||
|
|
26a827ea94 |
Name an unnamed print stage "Preparing" on the card
New printers report stage numbers before Bambuddy learns their names,
and the H2C still has several. Those reached the printer card verbatim,
as "Unknown stage (72)" -- a number that means nothing to the person
reading it, on the one line that otherwise says what the printer is
doing.
Every stage that has turned out to be unnamed so far has been part of
the run-up to printing, so an unnamed one now reads as "Preparing".
That is the same literal stage 74 already carries rather than a second
spelling of the same idea, so a card cannot show two different words
for the same situation depending on which number the firmware picked.
Display only, and deliberately not pushed down into get_stage_name.
That function also feeds the stage-transition log line and the
once-per-session warning added to capture unnamed stages so they can be
named in a later release; there the number is the entire diagnostic
value, and replacing it with "Preparing" would hide the only thing that
reports these. Both paths are pinned by tests asserting they disagree
for an unnamed stage and agree for a named one, so a later tidy-up
cannot quietly collapse them.
The idle sentinels are untouched: 255 on A1/P1 and -1 on X1 mean "no
stage", not an unnamed one, and still resolve to nothing rather than
being swept up by the fallback.
Nothing keys logic off stg_cur_name -- it is display-only in the printer
card, the print dialog's printer selector and the stream overlay -- so
all three improve and none change behaviour. No i18n either way: the
whole stage table has always been English.
|
||
|
|
2d0eca9354 |
Paint an AMS slot card with the spool's colours, not the tray's (issue #2967)
A Ziro "Colorful Mist" -- yellow, cyan and pink, effect Tri Color --
hovered on the printer card as a single flat pink rectangle. A printer
reports exactly one tray_color hex per tray and nothing else, so
telemetry cannot describe a gradient or a surface effect and never will.
The header now paints the bound spool's own swatch whenever that spool
declares extra colour stops or an effect, through buildFilamentBackground
-- the builder the Inventory swatches already use, so the two surfaces
cannot drift apart. A plain single-colour spool keeps the flat
backgroundColor it has always had, and a slot with nothing bound is
untouched, so the common case goes nowhere near the gradient path.
The gate is "any stop at all", not "more than one". buildColorLayer
ignores rgba the moment stops exist, so a one-stop spool renders that
stop rather than the slot hex; skipping it would leave this card showing
a different colour from the Inventory row for the same spool, which is
the class of disagreement the shared builder exists to prevent.
isLightColor now tests the colour actually on screen. Once the spool's
swatch is painted the base is no longer the slot hex -- a single stop
replaces it outright, and an effect-only spool paints the spool's own
rgba -- so testing the slot hex would pick the text colour for a
background that is not there.
Above one band no single hex can decide legibility, and the name sits
dead centre where a multi-stop background is likeliest to change under
it. So a genuinely multi-band header puts the name on the same scrim the
vendor badge already uses. One stop, or an effect over one colour, still
vendor badge already uses. One stop, or an effect over one colour, still
leaves a real base colour to test and keeps the contrast rule it had.
Spoolman mode gains the gradient in the process. Spoolman has held the
stops in filament.multi_color_hexes all along and the label renderer has
been reading them for releases, but _map_spoolman_spool never returned
them -- so the identical roll registered in Spoolman rendered flat while
the internally-managed one did not. Both now share one parser rather
than reading the same field two ways.
What stays asymmetric is Spoolman's own limitation, and it is pinned by
a test rather than left to be rediscovered: Spoolman has no field for a
surface effect at all. Its only neighbouring field,
multi_color_direction, says how the stops are laid out, not that the
roll is silk or glitter. effect_type is therefore None for a Spoolman
spool instead of guessed at, and silk/sparkle/wood remain internal-only.
The other two halves of the report -- the header naming the colour
"White" instead of "Colorful Mist", and the print dialog offering
"A3: PLA (White)" -- were already fixed on dev by #2875 and by the
slot-naming change that landed the day after this was filed. Neither is
in 1.2.5.3, which is what the reporter is running.
|
||
|
|
b2dda1fa5f |
Record the colour a slice is actually printed in (issue #2977)
Every file the internal slicer produced came back with filament_colour
= #00AE42 whatever filament was picked: a green plate thumbnail, green
metadata, and a "Color mismatch" in the Print dialog against the AMS
slot the job had just been correctly mapped to.
A colour is not a property of a filament preset in either slicer. It
belongs to the project, and Bambu Studio and OrcaSlicer set it from the
plate in their GUIs -- so nothing was attached to the preset Bambuddy
sends by name, and the CLI fell back to its own compiled-in default,
which is Bambu green. None of the shipped BBL filament profiles define
filament_colour; the whole tree has zero occurrences.
default_filament_colour is not the answer on its own. Measured against
a 02.08.02.61 sidecar, a profile carrying only that still slices to
filament_colour ["#00AE42"] -- Bambu Studio consumes it in the GUI when
a project is created, not in --load-filaments. So it is read and
rewritten as filament_colour, which the same sidecar does honour: the
reporter's exact triplet returns ["#E8B00C"] when patched this way, and
the colour lands in both project_settings.config and slice_info.config,
which is what the thumbnail and the AMS mapping actually read.
Each filament row in the slice dialog gets a colour control, and the
resolved value flows through a chain: the user's pick, then the preset's
own default_filament_colour, then the colour that slot was designed with
in the source 3MF. A slot with none of the three is left untouched
rather than given a guess.
The designed colour is read from project_settings.config, not
slice_info.config. The latter records what the file was last sliced
with, which for a source that never carried a colour is #00AE42 itself
-- measured -- so using it would have been circular.
The control is offered on single-filament sources too, because an STL,
and equally a mesh-only 3MF exported from CAD, has no colour anywhere
else to inherit. That is the case this exists for, and it took three
shapes to make it visible: a swatch in the label row was identical to
the read-only dot multi-colour rows have carried for releases, and
adding the hex beside it only made it look like a caption. It now sits
beside the dropdown, styled like it and the same height, with the
swatch and hex wrapped in one label bound to the input so a click
anywhere on it opens the picker.
An untouched slot with no designed colour submits an empty string
rather than the control's displayed default. A sent colour outranks the
preset's own, so pinning the placeholder would silently discard the
real colour of an imported OrcaSlicer profile that carries one.
Slicer Pipelines pick up the same chain without carrying a colour of
their own.
Also adds a warning for a defect found while investigating this: a
filament preset whose name the sidecar's bundle cannot resolve is not
rejected. The CLI inherits nothing, falls back to its defaults for
every field, and returns a well-formed success -- measured, an
unresolvable name slices as filament_type ["PLA"] at nozzle_temperature
["200"] with filament_ids [""] and filament_vendor ["(Undefined)"], so
a PETG preset a sidecar image predates prints at PLA temperatures with
no diagnostic anywhere. Both signals are required together, which keeps
it off the two legitimate lookalikes: a hand-written profile that never
named a vendor still carries a real filament id, and a user's own cloud
preset carries a vendor while legitimately having no bundled id. The
file is kept rather than refused, unlike the missing start G-code of
whoever can see the temperatures.
|