Commit Graph
28 Commits
Author SHA1 Message Date
octoturge 23f157d07a Prefix generated sequence_id instead of offsetting it
A printer's own free-running counter for unsolicited messages is always
a plain numeric string, so a non-numeric prefix ("cxproxy-") rules out
collision with it outright, rather than a large numeric starting offset
merely making collision improbable. Confirmed sequence_id need not be
numeric (live test, ISO-timestamp-prefixed value round-tripped fine) and
re-verified the new prefixed IDs round-trip through get_version on both
P1P (v1) and H2C (v2).
2026-09-07 15:03:14 +00:00
octoturge 256463e29c Add get_calibration; fix a real 10KB packet-size ceiling; revert bad correlation-field experiment
Real bug found and fixed live: both rumqttc clients default to a 10KB
max packet size in both directions. A real H2C's get_version reply
(25625 bytes - it reports a lot of modules: dual AMS, AMS HT, six WTM
units, ...) blew straight through that on the v5 client and killed the
whole connection outright (a real MQTT protocol error, not a timeout).
Raised to 1MB on both clients - print.push_status reports can get large
too.

Also tried, and reverted after live-testing disproved it: replacing
request()'s sequence_id correlation with a homemade
continuum_correlation_id field, on the understanding that most replies
echo back arbitrary extra request fields. True in general, false for
get_version specifically - a real H2C's reply had exactly ["command",
"module", "sequence_id"], nothing extra survived, so every get_version
call started timing out. Reverted to sequence_id, which is what's
actually proven (twice now, on both protocol versions) to round-trip for
this command.

Added BambuPrinter::get_calibration(option) - print.calibration gets a
real, correlated reply (confirmed live: it does echo sequence_id) on
both protocol versions, per the schema fixes in continuum-schemas.
`option` is the request's own bitmask, left to the caller rather than
defaulled to "run everything" - this triggers a real physical
calibration sequence on the printer, not a read-only query like
get_version, so it hasn't been live-tested here without explicit
confirmation first.
2026-09-07 14:55:09 +00:00
octoturge 6ce60542c6 Add pushall + print_status cache; fix envelope, race, and matching bugs
PrintReport now flattens CommandEnvelope (sequence_id/command) -
push_status.schema.json composes the shared envelope like every other
Bambu message; the struct's old doc comment concluded otherwise from a
capture that turned out to be an incomplete example (also missing dozens
of other real fields), not proof the message lacks them.

Added BambuMqttSession's PrintStatusCache: the in-memory
print.push_status object, replaced wholesale on v2 (always full) or on a
v1 message with msg != 1 (forced/full), merged field-by-field otherwise
(v1's organic partial updates) via PrintReport::merge_from. Tracks
last_sequence_id to warn on a gap in the organic stream. BambuPrinter
gets pushall() (v1-only; pushing.pushall doesn't exist in v2's schema at
all) and print_status().

Three real bugs found and fixed via live testing against an actual P1P,
none of them guessable from the schema alone:
- pushall's reply doesn't echo the sequence_id we send - the printer
  stamps its own free-running counter regardless of what triggered it.
  request()'s ID correlation silently can't work here; first fix attempt
  added request_next_of_category (wait for the next message under a
  category, no ID needed).
- That undershot too: matching by category alone let an unrelated
  "print" message (that category carries many commands - gcode_line,
  pause, push_status, ...) satisfy the wait before the real push_status
  reply arrived, so pushall() returned Ok having consumed the wrong
  message - now keyed by (category, command).
- A real ordering race: handle_incoming_report woke a waiting oneshot
  before updating the print_status cache, so a caller could resume and
  check print_status() before the write landed - pushall() returned Ok
  immediately followed by print_status() still showing None. Fixed by
  applying the cache update before sending on any matched oneshot.

Superseded by real protocol knowledge before any of the above needed to
ship, though: push_status carries its own msg field (1 = automatic/
partial, 0 = forced/full) - the actual, reliable, self-describing signal
for which kind a message is, regardless of timing or which client (if
any) triggered it. PrintStatusCache now reads that directly instead of
inferring it from request correlation, and pushall() is plain fire-and-
forget (publish, no wait) since there's nothing left to correlate for -
confirmed live: msg reads correctly off a real P1P, and the cache
populates/updates correctly with no gap warnings across several organic
pushes. request_next_of_category is kept (a future case may still need
"no correlation ID at all" for real), just no longer used here.

Also: the poll loop now logs when it exits instead of returning silently
- that silence is exactly what made a real dropped MQTT connection to a
live H2C indistinguishable from "nothing happening" during testing.
2026-09-07 14:39:55 +00:00
octoturge 835a4ae778 Auto-pin certificates on connect; fix Custom TLS mode for real MQTT
connect() now calls ensure_trusted() before opening the MQTT session, so
a newly-added printer with no ca_cert_path configured just works: bundled
CA if it verifies, trust-on-first-connect auto-pin if it doesn't (P1P,
at least) - the same thing examples/auto_connect_bambu.rs already did by
hand, now automatic on every real connect.

Wiring that in immediately surfaced a real, second instance of a bug the
codebase already knew about: build_connector()'s BambuTls::Custom branch
used add_root_certificate on the pinned certificate - but ensure_trusted
pins a *leaf* cert (CN = the printer's serial number, issued by Bambu's
own CA, not a root), for byte-exact fingerprint comparison, not chain
validation. Handing that leaf to add_root_certificate asks OpenSSL to
treat it as its own trust anchor - exactly the "self-signed certificate
in certificate chain" failure test_tls_handshake's doc comment already
described, just not fixed at this second call site (the one the real
MQTT connection actually uses) until now. native_tls has no per-
certificate verification callback hook (unlike rustls), so Custom now
accepts any certificate at the TLS layer instead, relying on
ensure_trusted having already fingerprint-verified this exact printer
moments before - weaker than continuous pinning on every reconnect, but
not a fresh regression: pinning a leaf as a root here never worked.

Verified live: deleted P1P's existing pin entirely, ran the daemon, and
watched it auto-fetch, pin, and connect on the very first attempt - the
freshly-pinned certificate came back byte-identical to the one that was
there before. Both configured printers (P1P forced v1, H2C auto-detected
v2) now connect successfully from a cold start with zero manual steps.
2026-09-05 21:45:32 +00:00
octoturge 4add7c1248 Wire fleet::load into main.rs; fix request() reply unwrapping
main.rs never actually loaded printers.toml or connected anything -
fleet/printer were only ever exercised by standalone examples. Now
main() loads the fleet (path configurable via CONTINUUM_PRINTERS_PATH,
default "printers.toml") and connects every printer concurrently at
startup, logging success/failure per printer rather than failing
startup over one bad printer.

That surfaced a real bug the instant it ran against a live printer:
complete_pending() was sending the whole {"info": {...}} wrapper through
the pending oneshot, not the unwrapped body - BambuPrinter::get_version
deserializing that into GetVersionResponse failed with "missing field
module" even though module was right there, just one level deeper than
expected. Fixed by unwrapping the single category key before completing
the pending request, symmetric with how a request gets wrapped on the
way out.

Verified live against a real H2C (MQTT v5/BambuMQTT v2): connect,
get_version, and the full pending-request correlation all worked
end-to-end, returning real firmware versions for every module (main
board 01.01.05.00, both AMS units, AMS HT, etc). The other configured
printer (P1P, forced v1) failed with a real, expected TLS error - it
doesn't chain to the bundled CA, matching the already-documented P1P
quirk; needs the ca_cert_path/fetch_bambu_cert workflow, not a code fix.
2026-09-05 21:39:26 +00:00
octoturge 416ef6db07 Add BambuPrinter::get_version, the first real request/response round trip
Documents request()'s real constraint on BambuMqttSession directly (not
all Bambu commands map 1:1 to a reply, and even where one exists its
shape can differ by protocol version - e.g. pushing.pushall triggers an
ongoing print.push_status stream rather than a direct reply, and
print.calibration data lives inside push_status on BambuMQTT v1 but gets
its own report on v2 - so request() is only correct for genuinely
one-shot, sequence_id-correlated commands like info.get_version).

get_version itself is Bambu-only (no GenericPrinter equivalent, matching
test_tls_handshake/fetch_certificate/ensure_trusted's existing pattern):
builds the request via continuum-schema-instance, sends it through
BambuMqttSession::request, and parses the reply into the existing
hand-written GetVersionResponse - reusing the struct already validated
against real captured Bambu JSON in bambu_commands_demo.rs, rather than
introducing a second response representation.

Verified structurally (no real broker reachable from here - confirmed
this sandbox has no route into the printer's LAN at all, not just a
blocked port): connect fails gracefully against an unreachable host, and
get_version correctly reports "not connected" rather than panicking when
called without a live session.
2026-09-05 21:27:59 +00:00
octoturge 9643c8b685 Real MQTT wiring for Bambu: connect, auto-detect v5/v3.1.1, request/reply
Removes GenericPrinter::receive (two disconnected fire-and-forget stubs
that didn't actually give request/response) in favor of a real mechanism
in the new src/printer/bambu_mqtt.rs:

- BambuMqttSession wraps whichever rumqttc client a connection ended up
  using (v3.1.1's AsyncClient/EventLoop for BambuMQTT v1, v5's for
  BambuMQTT v2 - different types, different event/error shapes) behind
  one interface. `open()` respects ForceV1/ForceV2, and for Auto tries
  v5 first, falling back to v3.1.1 on any failure (see the module doc
  comment for why "any failure", not a precisely-matched version-
  rejection code - unverified against a live printer, no broker here to
  test against).
- Reuses BambuTls::build_connector (now pub(crate)) for rumqttc's TLS
  transport via TlsConfiguration::NativeConnector, instead of a second,
  separate TLS trust story - hence rumqttc now pulls in use-native-tls
  instead of its default rustls stack, so there's one TLS implementation
  in the binary, not two.
- request() publishes and waits (with a timeout) for a report whose
  sequence_id matches, correlated via a shared pending-request table
  that a spawned background task completes as reports arrive - the
  first real tokio::spawn in this crate, flagged as deferred since
  printer/mod.rs's very first doc comment; this is why it was deferred
  until now, and why it's needed now (polling the eventloop has to run
  continuously, independent of whatever call site is waiting).
- publish()/publish_raw() are the fire-and-forget half, for commands
  like gcode_line that only get acknowledged implicitly.

BambuPrinter::connect/send_gcode_line/publish now call the real session
instead of printing what they would do. Verified against 192.0.2.1 (a
reserved, never-routable test address): both AsyncClient/EventLoop pairs
actually attempt a real TCP connection and fail gracefully (network
timeout, not a panic) when nothing answers, and Auto genuinely attempts
both while ForceV1 skips straight to v3.1.1.
2026-09-05 21:04:39 +00:00
octoturge b352fd618d Add upload_project_file/send_gcode_line, real Marlin serial, Bambu protocol auto-detect
- GenericPrinter grows upload_project_file, send_gcode_line, and a
  generic upload_gcode<P> driver that streams a whole gcode file line by
  line through any vendor's send_gcode_line.
- PrusaSerialPrinter's connect/send_gcode_line are real (not stubs): a
  held-open MarlinSerialConnection (src/printer/marlin_serial.rs) writes
  each line and waits for Marlin's "ok" before sending the next. Config
  changed from com_port: i16 to port: String + baud_rate: u32 to hold a
  real device path.
- Bambu: BambuGenericPrinter + BambuV1Printer/BambuV2Printer collapsed
  into one BambuPrinter, since which MQTT/BambuMQTT version (v3.1.1/v1
  vs v5/v2) a printer speaks is discovered at connect() time (or forced),
  not something config should pick a type for - the same physical
  printer can change after a firmware update. printers.toml gets a new
  "bambu" vendor (auto-detect, the default) alongside bambu_v1/bambu_v2
  (force a specific one).
- Bambu's gcode_line request is now built for real via
  continuum-schema-instance against continuum-schemas' JSON Schema,
  picking the v1 or v2 schema directory based on the detected protocol
  version, instead of a hand-written serde struct.
- Added rumqttc as a dependency (not wired up yet - the real MQTT
  v5-then-v3.1.1 fallback connect and the publish/receive plumbing are
  still stubs).
2026-09-05 20:40:21 +00:00
octoturge a2d4e578ec Replace em dashes with hyphens across docs and comments 2026-08-29 08:13:54 +00:00
octoturge 1583f531fa Add print.rs (AmsState/AmsUnit/AmsTray/XcamSettings), fix print vs pushing
Mirrors continuum-schemas' schemas/bambulab/ restructuring: PushallResponse
removed (it never existed on the wire — pushall's request goes to
"pushing", but the state-push stream it triggers arrives under "print",
a different root key, not a direct reply). BambuReport::Pushing(...) ->
BambuReport::Print(PrintReport).

AmsTray is an untagged enum (Empty/Loaded) rather than one struct full of
Option<T> fields, matching the JSON Schema's oneOf for the same slot -
#[serde(deny_unknown_fields)] on EmptyTray is load-bearing: untagged enums
try variants in order, and a loaded tray (superset of Empty's one field)
would otherwise incorrectly match Empty first.

Verified against the full real ams/xcam example from the conversation via
examples/bambu_commands_demo.rs - both the empty and loaded tray in that
payload deserialize to the correct enum variant.
2026-08-28 23:19:20 +00:00
octoturge 4eef52714b Move bambulab JSON Schemas to continuum-schemas (moved+restructured there)
Schema definitions belong in the schemas repo, not the service that
happens to be their only current consumer — see continuum-schemas'
commit for the restructuring (envelope.schema.json unversioned at
bambulab/ root, v1/ split into request/ and report/ subdirectories).
2026-08-28 22:58:34 +00:00
octoturge 42eb0b62d5 Add JSON Schema documentation for bambulab/v1, with real inheritance
Mirrors src/printer/bambu_commands/ as language-agnostic schema docs (not
used for codegen — the Rust structs stay hand-written). envelope.schema.json
is the base; get_version/pushall request+response schemas extend it via
allOf + $ref, which is genuinely closer to real inheritance than anything
else in this project's schema stack: proto and Rust only ever gave us
composition (embed a field holding the base struct), never a mechanism
that reuses a *referenced* schema's constraints automatically.

Verified with the jsonschema Python library, not just written by hand:
all three real examples from the conversation validate correctly, and —
importantly — negative tests confirm the base schema's required fields
and the command consts are actually enforced through the allOf chain, not
just documented. README covers the one real gotcha (additionalProperties:
false on a base schema silently breaks allOf composition, since allOf
validates each sub-schema against the whole instance independently rather
than merging them).
2026-08-28 22:53:59 +00:00
octoturge 166761479b Replace proto-based bambu_protocol with plain-Rust bambu_commands/
One file per command (get_version.rs, pushall.rs), each holding its
request AND response together. CommandEnvelope (sequence_id + command) is
the shared-fields piece every command embeds via #[serde(flatten)] — the
'inheritance' replacement for these structs, same composition idea as
BambuGenericPrinter in bambu.rs, just expressed as a struct field instead
of impl-block delegation.

examples/bambu_commands_demo.rs round-trips both commands against Bambu's
exact real JSON (from the original conversation) and confirms byte-for-
byte matches, same as the removed proto version did — this replacement is
behaviorally identical, just without protoc/build.rs in the loop.
2026-08-28 22:49:06 +00:00
octoturge 8e5d85202f Wire InfoCommand through as one real, runnable example
BambuRequest::Info(InfoCommand) is now live (was commented out) —
examples/bambu_protocol_demo.rs constructs one, serializes it, and prints
it next to Bambu's real wire JSON for comparison. Run it and copy this
exact shape (proto message -> use it here -> add an enum variant) for
every other command.
2026-08-28 22:41:55 +00:00
octoturge 6e18215af5 Add GenericPrinter::set_fan_speed() across all five printer kinds
Same override pattern as connect(): required, no default body, five
completely different bodies.

- BambuGenericPrinter::set_fan_speed_impl() is shared by BambuV1Printer and
  BambuV2Printer (composition reuse, same as connect_impl) - both speak
  the same MQTT gcode-injection mechanism.
- PrusaLinkPrinter and PrusaSerialPrinter both send the same M106 gcode
  but over different transports (HTTP command injection vs. raw serial
  bytes) - they share the percent->PWM conversion despite having unrelated
  connect() implementations.
- KlipperPrinter uses a hypothetical Moonraker-native endpoint that takes
  a percentage directly - no PWM conversion at all, since that math only
  applies to the gcode-speaking vendors.

percent_to_pwm() lives in mod.rs as a private free function rather than a
trait default: it's genuine shared logic, but only for the subset of
vendors that need it, which is exactly the case a trait default can't
express cleanly. Visible to bambu.rs/prusa.rs via super:: because Rust's
module privacy reaches into child modules.

Verified with cargo check --all-targets and a full run of
printer_polymorphism: 50% converges on the same PWM value (127) across all
three gcode-based printers, Klipper's native path takes 0.50 directly.
2026-08-28 21:40:11 +00:00
octoturge aa471ff906 Fix real bug: pinning a device leaf cert via add_root_certificate is unreliable
This broke against a real P1P: after auto-pinning, the immediate retry
failed with 'self-signed certificate in certificate chain' / 'unable to
get local issuer certificate' - the exact error OpenSSL gives when a
trust-anchor certificate isn't a well-formed CA, or when a device presents
a chain (leaf + its own separate self-signed root) that doesn't terminate
at whatever got pinned. My earlier test only covered a leaf that happened
to have CA:TRUE (openssl req -x509's default), which masked this.

Fixed by switching Custom(pem) from chain validation to true fingerprint
pinning: connect with verification off, then byte-compare the certificate
actually presented (via to_der()) against the pinned bytes, rather than
asking OpenSSL's PKI path-builder to accept an arbitrary leaf as a root.
BundledCa/Insecure are untouched - this only affects the Custom path.

Reproduced the exact failure locally (a leaf signed by a separate
self-signed root, served as a 2-cert chain - a properly non-CA leaf, not
my earlier accidentally-CA:TRUE test cert) before fixing it, then verified
against that same repro: first-run auto-pin now succeeds, second run is
silent, and - regression check - presenting a genuinely different
certificate after pinning still correctly fails, with a clear message
instead of an opaque OpenSSL error.
2026-08-28 21:33:05 +00:00
octoturge a77e93940f Fix ensure_trusted(): pin by serial number, not the arbitrary id label
id in printers.toml is just a label you chose for config purposes - nothing
ties it to a specific physical printer, and renaming it (or reusing it for
a different unit down the line) would silently break the pin lookup and
re-trigger trust-on-first-connect for hardware that was already trusted.
The serial number is the one thing about a printer that can't change, so
that's what a pin should be keyed by: certs/pinned/<sn>.pem instead of
certs/pinned/<id>.pem.

Verified against a real TLS server: pinned a printer under one id, renamed
it in printers.toml with the sn left unchanged, and confirmed the second
run found the existing pin silently (no re-TOFU) rather than re-pinning.
2026-08-28 21:22:16 +00:00
octoturge af2f20a45b Add BambuGenericPrinter::ensure_trusted() — smooth auto-connect, SSH-style
The manual fetch_bambu_cert workflow works but needs two commands and a
printers.toml edit. ensure_trusted() collapses that into one call, using
the same trust model SSH uses for host keys:
- a pin already on disk (certs/pinned/<id>.pem) is used directly, no new
  trust decision is made on every run
- no pin yet + bundled CA verifies fine (current-gen printers): nothing to
  do
- no pin yet + bundled CA fails (P1P, etc.): auto-pins via
  trust-on-first-connect, same as fetch_bambu_cert, but automatic
- an *explicit* pin (you set ca_cert_path yourself) never gets silently
  auto-pinned over — a failure there is a real error

examples/auto_connect_bambu.rs demonstrates the one-command flow.

Verified all three states against local test servers, not just compiled:
first run with no pin auto-pins and connects; second run against the same
server is silent (no re-TOFU message) and just verifies against the pin;
third run after swapping the server's certificate correctly FAILS instead
of silently re-pinning — confirms the security property survives the
smoother UX.
2026-08-28 21:18:46 +00:00
octoturge 88603b58c3 Fix Bambu TLS: relax hostname/IP verification, keep chain trust enforced
Real hardware surfaced this: H2C failed with 'IP address mismatch', not a
chain-of-trust error like P1P's 'self-signed certificate'. Bambu printer
certs don't carry their DHCP-assigned LAN IP as a SAN, so native-tls's
default hostname check will never pass against a real printer.

Added danger_accept_invalid_hostnames(true) unconditionally in
build_connector() — this only skips the SAN/IP match, it's independent from
danger_accept_invalid_certs (chain trust), which stays fully enforced for
BundledCa/Custom. Verified both directions against local test servers, not
just compiled:
- a cert signed by a trusted CA, with a SAN that does NOT match the
  connecting IP, now verifies OK (previously failed exactly like H2C did)
- a cert signed by an UNTRUSTED CA still correctly fails verification,
  confirming this change didn't weaken chain-of-trust checking
2026-08-28 21:13:28 +00:00
octoturge 8572d564b1 Split BambuPrinter into BambuGenericPrinter -> BambuV1Printer/BambuV2Printer
Completes the GenericPrinter -> BambuGenericPrinter -> BambuV1/V2 chain this
project wanted from the start. BambuGenericPrinter holds the fields and
behavior every Bambu printer shares (access code, serial number, CA trust,
the TLS test/fetch methods); BambuV1Printer and BambuV2Printer each *have*
one (composition) and are now genuinely separate types, ready to carry
flavour-specific report-schema fields later. Also threads through a new
'sn' (serial number) field the real MQTT topics will need.

Adds real 'fetch the CA automatically' capability, verified against a live
TLS server (not just compiled):
- BambuGenericPrinter::fetch_certificate() does trust-on-first-connect —
  connects once with verification disabled, captures the certificate the
  printer actually presents via native_tls's peer_certificate()/to_der(),
  and returns it as PEM. Deliberately a method you call once by hand
  (examples/fetch_bambu_cert.rs), not something connect() falls back to
  silently, since TOFU trusts whoever's on the network the moment you run
  it. Verified end-to-end against a local openssl s_server: the fetched
  PEM's SHA-256 fingerprint exactly matched the server's real certificate.
- Verified the other direction too: test_bambu_certs (bundled-CA mode)
  correctly REJECTS that same test server's cert, since it wasn't signed
  by the real Bambu CA.

Splitting BambuV1Printer/BambuV2Printer into distinct types broke the
PrinterHandle::BambuV1(x) | PrinterHandle::BambuV2(x) or-pattern in both
examples (or-patterns require every alternative to bind the same type) —
fixed by giving each variant its own match arm.
2026-08-28 21:04:56 +00:00
octoturge d8b7430296 Add printers.toml (gitignored) + real TLS cert test against actual hardware
New:
- printers.example.toml (committed template) / printers.toml (gitignored,
  real hosts + access codes don't belong in git) — a list of real printers
  with vendor, host, access_code/api_key/com_port, and an optional
  per-printer CA override.
- src/fleet.rs loads that file into ready-to-use PrinterHandles, matching
  vendor strings to the right constructor.
- BambuPrinter::test_tls_handshake() does a real (blocking, one-shot) TLS
  handshake to port 8883 using that printer's configured BambuTls trust
  mode — no MQTT protocol, just 'does the certificate verify'. Added
  native-tls and toml as direct dependencies for this.
- examples/test_bambu_certs.rs loads printers.toml and runs the handshake
  test against every Bambu entry.

Also fixes a real bug found while testing against unreachable IPs: plain
TcpStream::connect has no timeout and hung indefinitely on an offline
printer — switched to connect_timeout (5s).

Verified with cargo check --all-targets (0 errors) and by actually running
test_bambu_certs against a local printers.toml (correctly errored on a
missing cert file, then correctly timed out against unreachable test IPs
instead of hanging).
2026-08-28 20:48:05 +00:00
octoturge 72ba1e1cdc Bambu CA trust is per-printer, not per-fleet — P1P needs its own cert
Checked BambuStudio's resources/cert/ directly: it ships exactly one
LAN-mode CA (the bundled bambu_ca2.pem) plus an unrelated cloud-API leaf
cert — no second file to bundle for P1P. So a P1P's certificate is a
genuine per-device case, same as older units' 'download it from the
printer' flow, and a single Config-wide override path can't express 'most
printers use the bundled CA, but this one doesn't'.

Config.bambu_ca_cert_path (Option<PathBuf>) -> bambu_ca_cert_overrides
(HashMap<printer_id, PathBuf>), parsed from a comma-separated
CONTINUUM_BAMBU_CA_CERT_OVERRIDES env var. BambuTls::from_config renamed
to ::resolve to make clear it's called once per printer with that
printer's own override, not once for the whole config.

Verified with cargo check --all-targets and a full run of
cargo run --example printer_polymorphism.
2026-08-28 20:42:41 +00:00
octoturge 9eab5e5a04 Bambu TLS trust config + finish PrusaLink/PrusaSerial split
Certs:
- Vendor Bambu's shared LAN-mode root CA (certs/bambu_ca2.pem, verified
  self-signed CA:TRUE, see certs/README.md for provenance/fingerprint).
- Config gains bambu_ca_cert_path (per-printer override) and
  bambu_require_valid_cert (the allow/reject flag); printer::bambu::BambuTls
  turns those into BundledCa/Custom/Insecure. connect() doesn't perform a
  real handshake yet (no TLS-capable MQTT client wired in), just reports
  which trust mode it would use.

Also reconciles a rename in flight (Printer -> GenericPrinter trait) and
finishes the PrusaPrinter -> PrusaLinkPrinter/PrusaSerialPrinter split:
added the missing GenericPrinter impl for PrusaSerialPrinter, fixed
PrinterHandle's variant payload types, updated mod.rs's pub use list and
doc comments, and updated the example to the new 5-variant shape.

Verified with cargo check --all-targets (0 errors) and a full run of
cargo run --example printer_polymorphism.
2026-08-28 20:38:10 +00:00
octoturge d0a17ee50e Simplify boilerplate for learning: drop real protocol code, minimal main.rs
Removed adapters/ (rumqttc MQTT, suppaftp FTPS, reqwest HTTP), plate_changer/
(tokio-serial), discovery/, and cache.rs (rusqlite) — src/printer/ already
covers 'talk to a printer' as simple stubs, so keeping a second, more complex
version alongside it was redundant. main.rs goes from 6 concurrent tasks to
2 (uplink + go2rtc). uplink/ drops channels, jitter, and Send-bound futures.

Verified with cargo build + cargo run --example printer_polymorphism.
2026-08-28 18:22:38 +00:00
octoturge 5705f36c01 Simplify printer/ module for someone learning Rust
Drop reqwest, RPITIT+Send signature, and multi-variant error enum from the
teaching example so the only new idea per file is the trait override itself.
Real networking stays in adapters/, unaffected.
2026-08-28 18:04:13 +00:00
octoturge 9b3815fd25 Fix build errors: FTPS AsyncRead bridge, TLS connector type, unsafe env::set_var, dead code
Verified with cargo check + cargo run --example printer_polymorphism.
2026-08-28 17:09:11 +00:00
octoturge 03bd07fbd7 Add Printer trait + PrinterHandle enum domain model with polymorphism example 2026-08-28 17:03:21 +00:00
octoturge 2807b2b067 Initial boilerplate scaffold for continuum-proxy 2026-08-28 16:25:26 +00:00