mirror of
https://github.com/BetterCorp/BetterFrame.git
synced 2026-05-26 23:26:34 +00:00
- bf-status: query kiosk state by ID via /api/admin/kiosks/:id - bf-trigger-status: dedicated heartbeat-only topic kiosk.status (skips connect/disconnect noise from kiosk.changed) - bf-snapshot: GET /admin/entities/:id/snapshot as Buffer for motion → email/telegram flows - coordinator-ws now forwards both kiosk.changed (event=heartbeat) AND kiosk.status on every status message
59 lines
2.2 KiB
JavaScript
59 lines
2.2 KiB
JavaScript
/**
|
|
* bf-snapshot — fetch a single JPEG frame for a camera entity.
|
|
*
|
|
* GETs /admin/entities/:id/snapshot, which pulls one frame from the entity's
|
|
* main RTSP stream via ffmpeg/gst on the server. Returns image/jpeg or 502.
|
|
*
|
|
* On success: msg.payload = Buffer (binary JPEG), msg.contentType = "image/jpeg".
|
|
* On 502 / network error: the node errors out with done(err) and shows red.
|
|
*
|
|
* config.entity_id: numeric (overridable by msg.entity_id)
|
|
*
|
|
* Typical use: motion event → bf-snapshot → email / telegram / save-to-disk.
|
|
*/
|
|
module.exports = function (RED) {
|
|
function BfSnapshotNode(config) {
|
|
RED.nodes.createNode(this, config);
|
|
const node = this;
|
|
const cfg = RED.nodes.getNode(config.config);
|
|
|
|
node.on("input", async (msg, send, done) => {
|
|
if (!cfg || !cfg.server_url || !cfg.api_key) {
|
|
node.status({ fill: "red", shape: "ring", text: "missing bf-server-config" });
|
|
return done(new Error("bf-server-config server_url + api_key required"));
|
|
}
|
|
const entityId = msg.entity_id || config.entity_id;
|
|
if (!entityId) {
|
|
node.status({ fill: "red", shape: "ring", text: "missing entity_id" });
|
|
return done(new Error("entity_id required"));
|
|
}
|
|
const url = cfg.server_url + "/admin/entities/" + encodeURIComponent(String(entityId)) +
|
|
"/snapshot";
|
|
try {
|
|
const r = await fetch(url, {
|
|
method: "GET",
|
|
headers: {
|
|
authorization: "Bearer " + cfg.api_key,
|
|
accept: "image/jpeg",
|
|
},
|
|
});
|
|
if (r.status === 502) {
|
|
node.status({ fill: "red", shape: "ring", text: "no snapshot" });
|
|
return done(new Error("snapshot unavailable (HTTP 502)"));
|
|
}
|
|
if (!r.ok) throw new Error("HTTP " + r.status);
|
|
const ab = await r.arrayBuffer();
|
|
const buf = Buffer.from(ab);
|
|
msg.payload = buf;
|
|
msg.contentType = "image/jpeg";
|
|
node.status({ fill: "green", shape: "dot", text: String(buf.length) + " B" });
|
|
send(msg);
|
|
done();
|
|
} catch (err) {
|
|
node.status({ fill: "red", shape: "ring", text: err.message });
|
|
done(err);
|
|
}
|
|
});
|
|
}
|
|
RED.nodes.registerType("bf-snapshot", BfSnapshotNode);
|
|
};
|