Generate go2rtc's config from what the printers report
go2rtc has been supervised since before this commit, with nothing to serve: `CONTINUUM_GO2RTC_CONFIG` pointed at `./go2rtc.yaml`, and that file did not exist in the repository or anywhere else. A watchdog was restarting a process that had no cameras configured. `camera.rs` writes it, from the `rtsp_url` a printer publishes in its own `print.push_status` `ipcam` block rather than from `printers.toml`. The URL is already something the machine tells us, so asking an operator to keep a second copy of it - and to update it by hand when a printer's address changes - would be asking for drift. Two tasks, not one: the builder decides what should be served, the watchdog keeps the thing serving it alive. They meet at one `Notify`, because go2rtc reads its configuration only at startup, so a changed config needs a restart. The watchdog now selects the child's exit against that notification and skips its backoff on a deliberate change. What this does *not* cover is most printers, and that is the honest limit. Only the X1/H2 class publishes an RTSP URL - confirmed against two real machines: the H2C reports `rtsps://192.168.0.21:322/streaming/live/1`, the P1P reports an `ipcam` block with `ipcam_dev: "1"` and no URL at all. "A camera exists" and "a camera you can connect to" are different facts. A P1/A1 needs Bambu's proprietary port-6000 JPEG stream, which this crate does not speak, so those printers report no `camera_stream` and a UI should offer a player only when one is present. Details worth keeping: - The config is rendered sorted so an unchanged fleet is byte-identical, and on the first pass it is compared against the file on disk - without that, every daemon restart rewrites an identical file and drops every live viewer. - Written through a `.tmp` and renamed. The watchdog can restart go2rtc at any moment, and a half-written config fails to parse, which takes every working stream down rather than one. - Never logged, and gitignored: the credentials go in the URL because that is the only place go2rtc takes them for an RTSP source, so the file holds printer access codes. - A reported URL with no scheme is refused rather than repaired. One bad entry makes go2rtc fail to load the whole file. - No `rtsp:` or `webrtc:` block. The API listener is what serves WebRTC to a browser, which is the only consumer here; opening go2rtc's own RTSP server would publish every printer's camera on the LAN unauthenticated. - `stream_base_url` is reported in `hello`, not derived from the listen address: `:1984` is a bind address and not something a browser can open. Unset means "advertise nothing", which is the right default, because a wrong URL makes the UI offer a player that cannot connect. It is a LAN address either way - there is no relay, so this works for an operator in the workshop and not for anyone outside it. 63 tests, clippy unchanged at the two deliberate variant-size warnings, `cargo deny check licenses` ok. go2rtc itself stays operator-installed and is not distributed.
This commit is contained in:
@@ -123,6 +123,9 @@ pub struct PrintReport {
|
||||
/// Observed on v1 despite only the v2 schema declaring it; the v1
|
||||
/// schema was simply missing it.
|
||||
pub lights_report: Option<Vec<LightReport>>,
|
||||
/// The printer's camera settings - and, on the models that have one, the
|
||||
/// URL of its stream. See `Ipcam`.
|
||||
pub ipcam: Option<Ipcam>,
|
||||
|
||||
// There are still ~60 more top-level fields in a real v2 payload (see
|
||||
// `tests/fixtures/push_status_v2_h2c_printing.json` for the full set):
|
||||
@@ -193,9 +196,34 @@ impl PrintReport {
|
||||
self.stg = newer.stg.or(self.stg.take());
|
||||
self.vt_tray = newer.vt_tray.or(self.vt_tray.take());
|
||||
self.lights_report = newer.lights_report.or(self.lights_report.take());
|
||||
self.ipcam = newer.ipcam.or(self.ipcam.take());
|
||||
}
|
||||
}
|
||||
|
||||
/// The camera block, which is where a printer says whether it has a stream
|
||||
/// anything else can connect to.
|
||||
///
|
||||
/// Only `rtsp_url` is modelled of the dozen-odd fields in here, because it is
|
||||
/// the only one that is actionable: the rest are recording and timelapse
|
||||
/// preferences the printer manages itself.
|
||||
///
|
||||
/// **It is absent on most printers, and that is the headline.** Confirmed
|
||||
/// from live captures of two machines: an H2C reports
|
||||
/// `rtsps://<ip>:322/streaming/live/1`, while a P1P's block carries
|
||||
/// `ipcam_dev: "1"`, `ipcam_record: "enable"` and *no URL at all*. So the
|
||||
/// P1/A1 class has a camera that simply cannot be reached this way - it
|
||||
/// needs Bambu's proprietary port-6000 JPEG stream, which this crate does
|
||||
/// not implement. `ipcam_dev: "1"` means "a camera exists", not "a camera
|
||||
/// you can connect to", and reading it as the latter would have the gateway
|
||||
/// advertise streams that never play.
|
||||
#[derive(serde::Deserialize, Debug, Clone)]
|
||||
pub struct Ipcam {
|
||||
/// `rtsps://<ip>:322/streaming/live/1` on the models that have it. Note
|
||||
/// it carries no credentials: a client has to add `bblp:<access_code>`
|
||||
/// itself.
|
||||
pub rtsp_url: Option<String>,
|
||||
}
|
||||
|
||||
/// One light's state, as the printer reports it rather than as we asked for
|
||||
/// it. `mode` is the same vocabulary `ledctrl` accepts (`"on"`, `"off"`,
|
||||
/// `"flashing"`), and `node` the same light name - so a round-trip is
|
||||
|
||||
Reference in New Issue
Block a user