Authored by Claude (Opus 5 on UltraCode), approved by me - a human π
Hi β thanks for pycentauri. The provenance notes throughout are unusually good; sdcp.py's
"Reference implementations consulted" header and the PrintSpeedPct note ("Confirmed by reading the
printer's own SPA i18n file at /app/resources/www/assets/i18n/network-en.json") made it possible to
audit what the library actually puts on the wire before trusting it. That care is exactly why one
spot stands out.
(Updated after actually firing Cmd 128 β the original version of this issue was written before I
had run it. Empirical results are in Β§2.)
1. The ask: document the five extra keys in start_print()
Printer.start_print() (v0.9.0, client.py:416-424) sends a seven-field payload:
data = {
"Filename": filename,
"StartLayer": 0,
"Calibration_switch": 1 if auto_leveling else 0,
"PrintPlatformType": 0,
"Tlp_Switch": 1 if timelapse else 0,
"slot_map": [],
"path_prefix": path_prefix,
}
The published SDCP v3 spec documents only Filename and StartLayer for Cmd 128. The other five
carry no comment anywhere in the package β unlike almost every other non-obvious constant in the
codebase, which cites where it came from. Concretely, it would help to know:
Calibration_switch β what does 1 actually do? A full bed-mesh re-probe, a single
Z-touch, or something else? This is the one with real consequences: if it is a full re-probe,
running it with anything left on the plate could persist a corrupted mesh into every later print.
It also defaults to auto_leveling=True, so the riskier branch is the default. If that is
deliberate, saying why would be useful.
slot_map β sent as [] unconditionally. What is the element shape? (Partial empirical
answer in Β§2.)
PrintPlatformType β always 0. Are there other values (plate types)?
Tlp_Switch β presumably timelapse; confirmation that 0 is a clean no-op would be enough.
path_prefix β /local vs /usb, alongside the storage argument. Is it redundant with
Filename, or independently required?
Even a one-line comment each, in the same "confirmed against elegoo_fdm_cc_message_adapter.cpp"
style you already use, would fully resolve this. I'm not asking for a spec β just the provenance
you're clearly already tracking.
Why I care rather than just calling it and seeing: I'm wiring a deliberately over-gated remote
start for an unattended printer, and the failure mode is physical (the first act of a start is G28,
which finds Z by driving the nozzle into the plate).
2. Results from actually firing it β CC1 + CANVAS, firmware V1.4.46, pycentauri 0.9.0
The seven-field payload works as-is. start_print() returned Ack=0 and the print ran to
completion. So the five undocumented keys are at minimum accepted by CANVAS-era CC1 firmware, with
Calibration_switch: 0, PrintPlatformType: 0, Tlp_Switch: 0, slot_map: [],
path_prefix: "/local". I did not test a spec-minimal {Filename, StartLayer} payload and don't
intend to β I only wanted to confirm yours is not rejected.
Partial answer on slot_map: [] for a CANVAS machine. The gcode had T0 baked in (both
M6211 A1 L200 T0 β¦ and a bare T0), i.e. it requests slot 1. The CANVAS's active slot was 2.
The job fed from slot 2 β confirmed live from Cmd 324's active_tray_id mid-print.
β With an empty slot_map, the firmware resolves the feed to the currently active slot and does
not honour the gcode's baked-in tool index.
β οΈ Being precise about what that does and does not show: the active slot was already 2 before the
start, so this demonstrates "the gcode's T0 does not override the active slot" β not that an
empty slot_map positively selects anything. Separating those would need a run where the two
disagree in the other direction, which I have not done.
A verification note that may be useful for the library. PrintInfo.Status and
PrintInfo.Filename are unreliable as "did a new job start?" signals β the sub-status is sticky
after a job ends. PrintInfo.TaskId is a clean discriminator: empty string "" when idle, a real
UUID while printing. I gate post-send verification on TaskId changing rather than on the
sub-status.
3. Offer: Cmd 324 data from a real CC1 + CANVAS
canvas_status() currently raises, with this rationale (v0.9.0, client.py:265-278):
The SDK lists Cmd 324 (GET_CANVAS_STATUS) for CC1 SDCP but it is unprobed against real CC
firmware β and unknown Cmds can crash the CC1's app daemon, so pycentauri won't send it blind.
I have a CC1 on firmware V1.4.46 with a CANVAS attached, and have been sending Cmd 324 from my
own read-only client for weeks. It works, and has never crashed the app daemon β including
while a print was running. The reply shape:
Notes that might save you time:
- An empty slot returns garbage rather than nulls β mojibake in
brand / filament_name,
min/max_nozzle_temp: 0, and a stale-looking filament_color. Treat 0 temps as the emptiness
signal, not the strings.
filament_code is 0x00000 on every tray here, including loaded ones, so it does not appear
to carry a usable material identity on non-RFID spools.
- I would not trust
status without more evidence. I have seen 1 for loaded-but-idle slots and
2 for the active one, but have not established whether it reflects a mechanical sensor or a
GUI-declared value, and there are conflicting definitions around. Reporting what I observed, not
asserting semantics.
- β οΈ
active_tray_id is a firmware pointer, not a measurement. On my machine it kept pointing
at a slot that had already run empty during a failed auto-refill relay, and selecting a slot in the
GUI sets it with no check that filament is physically present. Worth a docstring warning if you do
implement this β it is easy to mistake for proof of what is loaded.
Happy to capture any specific frame, run a request/response pair, or open a PR implementing
canvas_status() for the CC1 against this shape β whichever is most useful. I'm deliberately not
opening a code PR unprompted since you may prefer a different approach for a command you've been
careful about.
4. Still not probed, and I won't guess
Whether the firmware returns Ack=1 BUSY for Cmd 128 while a job is running. That determines whether
there is a real firmware interlock behind start_print(), and testing it means risking a live print.
Best Regards - InfiniteBSOD
Updated at 2026-08-01
Authored by Claude (Opus 5 on UltraCode), approved by me - a human π
Hi β thanks for pycentauri. The provenance notes throughout are unusually good;
sdcp.py's"Reference implementations consulted" header and the
PrintSpeedPctnote ("Confirmed by reading theprinter's own SPA i18n file at
/app/resources/www/assets/i18n/network-en.json") made it possible toaudit what the library actually puts on the wire before trusting it. That care is exactly why one
spot stands out.
(Updated after actually firing Cmd 128 β the original version of this issue was written before I
had run it. Empirical results are in Β§2.)
1. The ask: document the five extra keys in
start_print()Printer.start_print()(v0.9.0,client.py:416-424) sends a seven-field payload:The published SDCP v3 spec documents only
FilenameandStartLayerfor Cmd 128. The other fivecarry no comment anywhere in the package β unlike almost every other non-obvious constant in the
codebase, which cites where it came from. Concretely, it would help to know:
Calibration_switchβ what does1actually do? A full bed-mesh re-probe, a singleZ-touch, or something else? This is the one with real consequences: if it is a full re-probe,
running it with anything left on the plate could persist a corrupted mesh into every later print.
It also defaults to
auto_leveling=True, so the riskier branch is the default. If that isdeliberate, saying why would be useful.
slot_mapβ sent as[]unconditionally. What is the element shape? (Partial empiricalanswer in Β§2.)
PrintPlatformTypeβ always0. Are there other values (plate types)?Tlp_Switchβ presumably timelapse; confirmation that0is a clean no-op would be enough.path_prefixβ/localvs/usb, alongside thestorageargument. Is it redundant withFilename, or independently required?Even a one-line comment each, in the same "confirmed against
elegoo_fdm_cc_message_adapter.cpp"style you already use, would fully resolve this. I'm not asking for a spec β just the provenance
you're clearly already tracking.
Why I care rather than just calling it and seeing: I'm wiring a deliberately over-gated remote
start for an unattended printer, and the failure mode is physical (the first act of a start is
G28,which finds Z by driving the nozzle into the plate).
2. Results from actually firing it β CC1 + CANVAS, firmware V1.4.46, pycentauri 0.9.0
The seven-field payload works as-is.
start_print()returnedAck=0and the print ran tocompletion. So the five undocumented keys are at minimum accepted by CANVAS-era CC1 firmware, with
Calibration_switch: 0,PrintPlatformType: 0,Tlp_Switch: 0,slot_map: [],path_prefix: "/local". I did not test a spec-minimal{Filename, StartLayer}payload and don'tintend to β I only wanted to confirm yours is not rejected.
Partial answer on
slot_map: []for a CANVAS machine. The gcode hadT0baked in (bothM6211 A1 L200 T0 β¦and a bareT0), i.e. it requests slot 1. The CANVAS's active slot was 2.The job fed from slot 2 β confirmed live from Cmd 324's
active_tray_idmid-print.β With an empty
slot_map, the firmware resolves the feed to the currently active slot and doesnot honour the gcode's baked-in tool index.
start, so this demonstrates "the gcode's
T0does not override the active slot" β not that anempty
slot_mappositively selects anything. Separating those would need a run where the twodisagree in the other direction, which I have not done.
A verification note that may be useful for the library.
PrintInfo.StatusandPrintInfo.Filenameare unreliable as "did a new job start?" signals β the sub-status is stickyafter a job ends.
PrintInfo.TaskIdis a clean discriminator: empty string""when idle, a realUUID while printing. I gate post-send verification on
TaskIdchanging rather than on thesub-status.
3. Offer: Cmd 324 data from a real CC1 + CANVAS
canvas_status()currently raises, with this rationale (v0.9.0,client.py:265-278):I have a CC1 on firmware V1.4.46 with a CANVAS attached, and have been sending Cmd 324 from my
own read-only client for weeks. It works, and has never crashed the
appdaemon β includingwhile a print was running. The reply shape:
{ "auto_refill": 1, // 0/1 β reflects the touchscreen toggle "active_canvas_id": 0, "active_tray_id": 1, // 0-BASED: tray 1 == CANVAS slot 2 in the UI "canvas_list": [ { "canvas_id": 0, "connected": 1, "tray_list": [ { "tray_id": 0, // 0-based "brand": "ELEGOO", "filament_type": "PETG", "filament_name": "PETG-CF", "filament_code": "0x00000", "filament_color": "#000000", "min_nozzle_temp": 240, "max_nozzle_temp": 270, "status": 1 // see caveat below } // ... 4 trays total ] } ] }Notes that might save you time:
brand/filament_name,min/max_nozzle_temp: 0, and a stale-lookingfilament_color. Treat0temps as the emptinesssignal, not the strings.
filament_codeis0x00000on every tray here, including loaded ones, so it does not appearto carry a usable material identity on non-RFID spools.
statuswithout more evidence. I have seen1for loaded-but-idle slots and2for the active one, but have not established whether it reflects a mechanical sensor or aGUI-declared value, and there are conflicting definitions around. Reporting what I observed, not
asserting semantics.
active_tray_idis a firmware pointer, not a measurement. On my machine it kept pointingat a slot that had already run empty during a failed auto-refill relay, and selecting a slot in the
GUI sets it with no check that filament is physically present. Worth a docstring warning if you do
implement this β it is easy to mistake for proof of what is loaded.
Happy to capture any specific frame, run a request/response pair, or open a PR implementing
canvas_status()for the CC1 against this shape β whichever is most useful. I'm deliberately notopening a code PR unprompted since you may prefer a different approach for a command you've been
careful about.
4. Still not probed, and I won't guess
Whether the firmware returns
Ack=1 BUSYfor Cmd 128 while a job is running. That determines whetherthere is a real firmware interlock behind
start_print(), and testing it means risking a live print.Best Regards - InfiniteBSOD
Updated at 2026-08-01