Skip to content

Control plane, A1 support, camera view and slicer fix: bring main up to the deployed gateway - #6

Merged
cyrilcaoyang merged 17 commits into
mainfrom
fix/chamber-sensor-placeholder
Oct 21, 2026
Merged

cyrilcaoyang merged 17 commits into
mainfrom
fix/chamber-sensor-placeholder

Conversation

@cyrilcaoyang

@cyrilcaoyang cyrilcaoyang commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Why

The deployed gateway runs this branch, not main. This PR brings main up to what is deployed: the human-approved control plane (2026-10-04), STL slicing, the chamber light, planning docs, and the work done 2026-10-20/21 to add and print on the Bambu A1.

What's in it

Control plane and slicing (2026-10-04/05): STATUS_SPEC v1.1 claims, start_print, stop_print, camera snapshot and chamber light, all gated in bambu_server.dispatch. Headless Bambu Studio slicing. AMS HT trays are refused until their tray index is verified. Dispatch re-reads the artifact instead of trusting intake facts.

Approved gate changes (each recorded in AGENT_RULES.md, AGENTS.md and docs/CONTROL_PLANE_DESIGN.md):

  • 2026-10-20, external spool (vt_tray, id 254) as a mapping target for printers without an AMS. It gets the same material and nozzle-window checks, is single-filament only, and is sent as use_ams: false with mapping [254].
  • 2026-10-20, stock bed-mesh M500 accepted only directly after G29 inside the machine start gcode, confirmed by the end marker. Every other M500 is still refused.
  • 2026-10-21, view-only camera, GET /printers/{id}/camera.jpg: no claim, no command, one shared read per printer every 2 s, never plate-check evidence. It amends rule 3.
  • 2026-10-21, no manual snapshot step: a camera start_print captures and records its own frame under the claim when start is pressed, and refuses with 502 if the camera delivers none.

Fixes:

  • The slicer now flattens inherits and include presets. Before this, CLI slices silently got a 200 × 200 bed, a generic G28 start gcode and generic temperatures. The 2026-10-05 P1S test job carried the same broken start.
  • The placeholder chamber reading (fixed 5 C) is dropped on P1P/P1S/A1/A1 mini.
  • The external spool's placeholder "0% remaining" is dropped.

Page:

  • The printer dropdown, Start camera and light share one bar, with a fixed-size camera frame beside the detail tiles. The camera is off until started.
  • One Submission section: the form plus a thumbnail preview on the left, and the checklist on the right, always shown.
  • No manual snapshot step.
  • The signed-in banner and name field are hidden for verified users.
  • A standalone visit under /bambu redirects to the dashboard panel.

Hardware results (2026-10-21, A1 in LAN Only + Developer Mode)

  • adptor.gcode.3mf (external spool, stock M500): upload verified, and the printer confirmed RUNNING 8 s after the start command. It printed to the end, but the part detached (no brim, a first layer of thin walls only). The gateway reports "finished" for a failed print; it has no failure detection.
  • 3DBenchy (external spool, flattened-preset slice, textured plate): RUNNING confirmed 12 s after the command, and it printed end to end, confirmed by camera snapshot.
  • Camera view: works on the P1S and A1. On the H2D: "camera frame is not a JPEG", because the H2 series uses a different camera protocol, so its plate check has to be in person.

Test plan

  • uv run pytest -q: 243 passed (fake backends only)
  • ruff check . is clean
  • Test printer in LAN Only + Developer Mode (A1)
  • Hardware: claim → plate check → start_print → RUNNING within the confirm timeout (twice)
  • One complete print end to end (3DBenchy)
  • stop_print partway through a print. Never exercised yet.
  • What the 2026-10-05 P1S job actually did. It was sliced with the broken generic start, which likely explains the failed start.
  • A camera start without a snapshot token, on hardware. Live after the next gateway restart.

🤖 Generated with Claude Code

cyrilcaoyang and others added 17 commits October 5, 2026 01:49
Adds a control plane, off unless `dispatch.enabled` is set, that takes a
job from mesh to a running print with a person in the loop:

- POST /submissions accepts .stl for printers with a slicer preset; the
  gateway slices with the Bambu Studio CLI, forcing the printer's nozzle
  type and declared build plate, then validates and queues the 3mf.
- STATUS_SPEC v1.1 claims per printer, and three claimed verbs under
  /printers/{id}/control/: snapshot (one camera frame), start_print,
  stop_print. allowed_actions comes from the same gate as the 412s.
- start_print requires an approved job, a cold idle printer, a matching
  declared plate, revalidation, an explicit tray per filament, and
  plate_confirmed_empty by camera snapshot or in person. Verified edge
  identities are required by default.
- Upload is size-verified on the printer; the job is running only when
  the printer reports it. One attempt per job, never retried; an
  unconfirmed or interrupted dispatch fails with its uncertainty recorded.
- /ui gains the Start and Stop flows; docs and agent rules record the
  approved scope.
HT units report ids from 128, and the flat ams_mapping index used for
standard units (ams_id * 4 + tray) is not known to be right for them.
The gate refuses an HT tray and the page no longer offers one.
Jobs inspected before plate and slot details were recorded carried empty
fields, and start_print refused them as "not a sliced .3mf". Dispatch
now inspects the stored file under the claim, so it checks what will
actually print and names the real reason when it refuses.
POST /printers/{id}/control/light switches the chamber LED under a claim,
in any observable state; allowed_actions advertises it from the same gate.
The /ui start panel gets a Light on/off button beside the snapshot. Homing
to raise the plate into view was declined and the reason recorded.
The first live start was uploaded and acknowledged but ignored by the
printer; firmware 01.10 in cloud mode most likely refuses LAN control.
Records the evidence, what users lose, a reply-capture step to confirm
the cause first, the switch-over, verification order, and rollback.
The P1 and A1 series have no chamber temperature sensor, but their firmware
still reports a fixed chamber_temper (5 C on the live P1S and A1). The
gateway passed it through as a metric, so the dashboard showed a 5 C chamber
on an open-frame A1.

Key the omission on the operator-declared model, matched exactly: a real
chamber can read 5 C, so the value alone is not evidence. An undeclared or
unlisted model keeps whatever the printer reports, so the H2D and X1C are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The A1 has no AMS: a spool is fed by hand and its filament selected on the
screen, which the printer reports as vt_tray. Dispatch only mapped AMS trays,
so the A1 could not be started at all, and intake rejected its jobs because
no loaded tray matched.

The external spool is now read like an AMS tray (allowlisted fields only) and
carried as AMS 254 tray 0 in observed.loaded_trays, so intake validation and
the dispatch mapping check it the same way: material match and the spool's
nozzle window. It is published as details.external_spool, apart from
ams_trays. A job fed from it must be single-filament, since the start
command's use_ams is all or nothing, and is sent as use_ams false with
mapping [254], as Bambu Studio does. The submission page offers it as
"External spool".

Approved by a human on 2026-10-20 as a dispatch gate change (AGENT_RULES 1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Current Bambu Studio's A1 start gcode saves the bed mesh right after probing:
"G29 ... / M400 / M500 ; save cali data". The gcode check refused every M500,
so every A1 job exported from current Bambu Studio was rejected at intake.

Accept exactly that M500: directly after a G29 (only M400 between), inside
the machine start gcode, confirmed by the slicer's MACHINE_START_GCODE_END
marker appearing afterwards. A tail scan never qualifies, and an M500 whose
marker is never seen is still refused, as is every other M500.

Approved by a human on 2026-10-20 as a narrowed gate (AGENT_RULES 1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A person wants to see the printer before submitting and while its job runs,
but the camera was reachable only through the claimed plate-check snapshot.

GET /printers/{id}/camera.jpg serves the chamber camera without a claim, only
when dispatch is enabled. It publishes nothing to the printer, reads the
camera at most once per printer every 2 s with the frame shared by all
viewers (failures cached the same way), and serializes camera reads with the
plate-check snapshot. Its frames carry no snapshot token, so they can never
satisfy the plate check.

The page shows it under Machine for the selected printer, off on load until
a person presses Show camera, refreshing every 2 s and pausing while the tab
is hidden.

Approved by a human on 2026-10-21, amending AGENT_RULES rule 3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Bambu Studio CLI does not resolve `inherits` (or the newer `include`
templates) for a settings file outside its bundled profiles. The slicer
copied the machine preset into the job dir with only the child's own keys,
so everything the parents define was silently replaced by CLI defaults: an
A1 slice came out with a 200 x 200 bed, a two-line G28 start gcode instead
of the A1's (no wipe, no bed leveling, no purge) and generic 200 C PLA. The
2026-10-05 P1S test job carried the same generic start.

Fold each preset's inherits chain and include templates into one
self-contained file, child values winning, for the machine, process and
filament alike. A fixed A1 slice now matches a Bambu Studio export apart
from version differences.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… who

The not-signed-in and no-identity warnings stay: those change what a
submission means.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The page is embedded in the dashboard's 3D Printers panel; opened on its own
under the edge prefix it now redirects there instead of being a second,
full-page copy. Direct gateway access (no prefix) still serves it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The camera sits in a 4:3 frame on the left of the Machine panel with the
detail tiles to its right (stacked, capped at 320 px, on narrow screens).
The frame keeps its size whether the camera is on or off; it shows
"Camera off" until a person presses Start camera.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed in through the dashboard, the edge identity is what gets recorded,
so the read-only "Signed in as" field only restated the sign-in. Visitors
who are not signed in still type a name. The camera frame grows from 280
to 420 px (480 px cap when stacked).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rame

With the live camera view on the page, the start panel's "Take snapshot"
step only repeated what the person had just looked at. A camera start_print
without a snapshot_token now has the gateway capture the frame itself, under
the claim, at the moment start is pressed, and keep its hash, time and image
with the job -- a fresher record than a snapshot taken minutes earlier. If
the camera delivers no frame the start is refused (502) and nothing is
uploaded; a snapshot_token from /control/snapshot is still accepted. The
person's plate_confirmed_empty statement is unchanged.

The start panel loses the snapshot box and button and points to the camera
view above; the light button stays.

Approved by a human on 2026-10-21 as a dispatch gate change (AGENT_RULES 1, 4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ecklist

- The printer dropdown, Start camera and the light share one bar; the
  "Machine" heading and "Target" label are gone. The light takes a claim
  only for the switch (or uses the start panel's) and gives it back.
- Artifact and Result become one Submission section: file, material, name
  and Submit on the left with a small preview of the thumbnail the slicer
  embedded (.3mf plate_N.png or the .gcode thumbnail block, read in the
  browser); the checklist on the right, always shown, pending until the
  file is submitted.
- The external spool's remaining percentage is dropped: a hand-fed spool
  has no sensor and the A1's 0 is a placeholder, not "empty".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cyrilcaoyang cyrilcaoyang changed the title Control plane, slicing, and docs: bring main up to the deployed gateway Control plane, A1 support, camera view and slicer fix: bring main up to the deployed gateway Oct 21, 2026
@cyrilcaoyang
cyrilcaoyang marked this pull request as ready for review October 21, 2026 03:11
@cyrilcaoyang
cyrilcaoyang merged commit a70cea6 into main Oct 21, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant