add camera video feed to bambu manager

This commit is contained in:
Stewart Allen 2025-02-03 16:37:02 -05:00
commit 97b5bd3947
5 changed files with 73 additions and 4 deletions

View file

@ -15,6 +15,7 @@ self.kiri.load(api => {
let init = false;
let status = {};
let monitors = [];
let video_on = false;
let bound, device, printers, select, selected, conn_alert;
let btn_del, in_host, in_code, in_serial, filelist;
let host, password, serial, amsmap, socket = {
@ -35,11 +36,28 @@ self.kiri.load(api => {
};
ws.onmessage = msg => {
let data = JSON.parse(msg.data);
let { serial, message, monitoring, files, found, deleted, error } = data;
let { serial, message, monitoring, files, found, deleted, frame, error } = data;
if (error) {
console.log({ serial, error });
api.alerts.show(`Bambu Error: ${error}`, 3);
// printer_status(`error: ${error}`);
} else if (frame) {
if (selected?.rec?.serial !== serial) {
console.log({
frame_serial_mismatch: serial,
current: selected?.rec?.serial
});
return;
}
const img = new Image();
img.src = `data:image/jpeg;base64,${frame}`;
img.onload = () => {
const canvas = $('bbl_video');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
};
} else if (deleted) {
console.log('file deleted', deleted);
file_list();
@ -149,7 +167,21 @@ self.kiri.load(api => {
});
}
function printer_video_toggle() {
printer_video_set(video_on = !video_on);
}
function printer_video_set(bool) {
set_frames(selected?.rec?.serial, bool);
ui.setVisible($('bbl_rec'), !bool);
ui.setVisible($('bbl_video'), bool);
const canvas = $('bbl_video');
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
function printer_select(name = '') {
printer_video_set(false);
btn_del.disabled = false;
let rec = printers[name] || {};
selected = { name, rec };
@ -290,6 +322,12 @@ self.kiri.load(api => {
return mon ? true : false;
}
function set_frames(serial, bool) {
if (serial) {
socket.send({ cmd: "frames", frames: bool, serial });
}
}
function cmd_if(cmd, obj = {}) {
if (monitoring()) {
socket.send({ ...obj, cmd, serial: selected.rec.serial });
@ -535,6 +573,15 @@ self.kiri.load(api => {
wrap: "off",
spellcheck: "false",
rows: 15, cols: 65
}),
h.button({
style: "position: absolute; top: 5px; right: 5px",
onclick: printer_video_toggle
}, [ h.i({ class: "fa-solid fa-video" }) ]),
h.canvas({
id: "bbl_video",
class: "video hide",
style: "width: 100%; height: 100%; box-sizing: border-box",
})
]),
h.div({ class: "f-col gap3" }, [

View file

@ -56,7 +56,7 @@ class FrameStream extends EventEmitter {
})
}
close() {
end() {
this.#remoteSocket.end();
}
}

View file

@ -17,6 +17,7 @@ module.exports = async (server) => {
#timer2;
#client;
#serial;
#frames;
#topic_report;
#topic_request;
#options = {
@ -59,6 +60,18 @@ module.exports = async (server) => {
client.on("error", error => onerror(error));
}
set_frames(bool) {
if (this.#frames && !bool) {
this.#frames.end();
this.#frames = undefined;
} else if (!this.#frames && bool) {
this.#frames = new FrameStream(this.#options.host, this.#options.password)
.on("frame", jpg => {
wsend({ serial: this.#serial, frame: jpg.toString('base64') });
})
}
}
keepalive() {
clearTimeout(this.#timer);
this.#timer = setTimeout(() => { this.end() }, 30000);
@ -319,7 +332,7 @@ module.exports = async (server) => {
wsend({ found });
ws.on('message', msg => {
msg = JSON.parse(msg);
let { cmd, host, code, serial, path, amsmap, direct } = msg;
let { cmd, host, code, serial, path, amsmap, direct, frames } = msg;
switch (cmd) {
case "monitor":
get_mqtt(host, code, serial, message => {
@ -391,6 +404,10 @@ module.exports = async (server) => {
case "direct":
if_mqtt(serial, direct);
break;
case "frames":
util.log('request frames', serial, frames);
mcache[serial]?.set_frames(frames);
break;
case "keepalive":
// util.log({ keepalive: serial });
mcache[serial]?.keepalive();

View file

@ -154,7 +154,7 @@ gapp.overlay(root, {
// add common element types
[
"a", "i", "hr", "div", "pre", "code", "span", "label", "input",
"button", "svg", "textarea", "select", "option"
"button", "svg", "textarea", "select", "option", "img", "canvas"
].forEach(type => {
h[type] = (attr, innr) => {
return h.el(type, attr, innr);

View file

@ -212,6 +212,11 @@ details[open] > summary::after {
transform: rotate(90deg);
}
.video {
border: 1px solid #888;
border-radius: 3px;
}
/* container for entire page / app */
#app {
position: fixed;