Serve P1/A1 camera frames over the uplink

A P1 or A1 has no RTSP endpoint; its camera is a TLS stream on port 6000.
bambu_camera logs in and reads one JPEG frame, and the uplink answers a
camera_snapshot_request with it. Telemetry advertises camera_snapshot for
printers that report a camera block but no RTSP URL.
This commit is contained in:
2026-10-29 15:55:21 +00:00
parent b4948076a0
commit 0c8ad9cc3d
7 changed files with 293 additions and 4 deletions
+27
View File
@@ -188,6 +188,7 @@ fn empty_telemetry_fields_are_omitted_not_null() {
// The common case, and the third field that must be omitted: a
// P1P reports that it has a camera without giving a URL for it.
camera_stream: None,
camera_snapshot: None,
}],
};
@@ -226,6 +227,7 @@ fn a_zero_stage_is_sent_rather_than_treated_as_absent() {
print_error: Some(0),
active_alerts: Some(0),
camera_stream: Some("p1p".into()),
camera_snapshot: None,
}],
};
@@ -299,3 +301,28 @@ fn a_hello_carries_no_credential() {
assert!(!json.contains("token"), "the hello must carry no token, got:\n {json}");
assert_eq!(json, r#"{"type":"hello","gateway_id":"gw-1","farm_id":"farm-1"}"#);
}
#[test]
fn a_snapshot_request_is_readable_and_its_answer_matches_the_schema() {
let request = r#"{"type":"camera_snapshot_request","request_id":"r1","printer_id":"p1p"}"#;
assert!(matches!(
serde_json::from_str::<ServerMessage>(request),
Ok(ServerMessage::CameraSnapshotRequest { .. })
));
let ok = serde_json::to_string(&ClientMessage::CameraSnapshot {
request_id: "r1".into(),
jpeg_base64: Some("/9j/".into()),
error: None,
})
.unwrap();
assert_eq!(ok, r#"{"type":"camera_snapshot","request_id":"r1","jpeg_base64":"/9j/"}"#);
let failed = serde_json::to_string(&ClientMessage::CameraSnapshot {
request_id: "r1".into(),
jpeg_base64: None,
error: Some("no frame".into()),
})
.unwrap();
assert_eq!(failed, r#"{"type":"camera_snapshot","request_id":"r1","error":"no frame"}"#);
}