add camera video feed to bambu manager
This commit is contained in:
parent
d1815d8103
commit
97b5bd3947
5 changed files with 73 additions and 4 deletions
|
|
@ -15,6 +15,7 @@ self.kiri.load(api => {
|
||||||
let init = false;
|
let init = false;
|
||||||
let status = {};
|
let status = {};
|
||||||
let monitors = [];
|
let monitors = [];
|
||||||
|
let video_on = false;
|
||||||
let bound, device, printers, select, selected, conn_alert;
|
let bound, device, printers, select, selected, conn_alert;
|
||||||
let btn_del, in_host, in_code, in_serial, filelist;
|
let btn_del, in_host, in_code, in_serial, filelist;
|
||||||
let host, password, serial, amsmap, socket = {
|
let host, password, serial, amsmap, socket = {
|
||||||
|
|
@ -35,11 +36,28 @@ self.kiri.load(api => {
|
||||||
};
|
};
|
||||||
ws.onmessage = msg => {
|
ws.onmessage = msg => {
|
||||||
let data = JSON.parse(msg.data);
|
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) {
|
if (error) {
|
||||||
console.log({ serial, error });
|
console.log({ serial, error });
|
||||||
api.alerts.show(`Bambu Error: ${error}`, 3);
|
api.alerts.show(`Bambu Error: ${error}`, 3);
|
||||||
// printer_status(`error: ${error}`);
|
// 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) {
|
} else if (deleted) {
|
||||||
console.log('file deleted', deleted);
|
console.log('file deleted', deleted);
|
||||||
file_list();
|
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 = '') {
|
function printer_select(name = '') {
|
||||||
|
printer_video_set(false);
|
||||||
btn_del.disabled = false;
|
btn_del.disabled = false;
|
||||||
let rec = printers[name] || {};
|
let rec = printers[name] || {};
|
||||||
selected = { name, rec };
|
selected = { name, rec };
|
||||||
|
|
@ -290,6 +322,12 @@ self.kiri.load(api => {
|
||||||
return mon ? true : false;
|
return mon ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function set_frames(serial, bool) {
|
||||||
|
if (serial) {
|
||||||
|
socket.send({ cmd: "frames", frames: bool, serial });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function cmd_if(cmd, obj = {}) {
|
function cmd_if(cmd, obj = {}) {
|
||||||
if (monitoring()) {
|
if (monitoring()) {
|
||||||
socket.send({ ...obj, cmd, serial: selected.rec.serial });
|
socket.send({ ...obj, cmd, serial: selected.rec.serial });
|
||||||
|
|
@ -535,6 +573,15 @@ self.kiri.load(api => {
|
||||||
wrap: "off",
|
wrap: "off",
|
||||||
spellcheck: "false",
|
spellcheck: "false",
|
||||||
rows: 15, cols: 65
|
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" }, [
|
h.div({ class: "f-col gap3" }, [
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ class FrameStream extends EventEmitter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
end() {
|
||||||
this.#remoteSocket.end();
|
this.#remoteSocket.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ module.exports = async (server) => {
|
||||||
#timer2;
|
#timer2;
|
||||||
#client;
|
#client;
|
||||||
#serial;
|
#serial;
|
||||||
|
#frames;
|
||||||
#topic_report;
|
#topic_report;
|
||||||
#topic_request;
|
#topic_request;
|
||||||
#options = {
|
#options = {
|
||||||
|
|
@ -59,6 +60,18 @@ module.exports = async (server) => {
|
||||||
client.on("error", error => onerror(error));
|
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() {
|
keepalive() {
|
||||||
clearTimeout(this.#timer);
|
clearTimeout(this.#timer);
|
||||||
this.#timer = setTimeout(() => { this.end() }, 30000);
|
this.#timer = setTimeout(() => { this.end() }, 30000);
|
||||||
|
|
@ -319,7 +332,7 @@ module.exports = async (server) => {
|
||||||
wsend({ found });
|
wsend({ found });
|
||||||
ws.on('message', msg => {
|
ws.on('message', msg => {
|
||||||
msg = JSON.parse(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) {
|
switch (cmd) {
|
||||||
case "monitor":
|
case "monitor":
|
||||||
get_mqtt(host, code, serial, message => {
|
get_mqtt(host, code, serial, message => {
|
||||||
|
|
@ -391,6 +404,10 @@ module.exports = async (server) => {
|
||||||
case "direct":
|
case "direct":
|
||||||
if_mqtt(serial, direct);
|
if_mqtt(serial, direct);
|
||||||
break;
|
break;
|
||||||
|
case "frames":
|
||||||
|
util.log('request frames', serial, frames);
|
||||||
|
mcache[serial]?.set_frames(frames);
|
||||||
|
break;
|
||||||
case "keepalive":
|
case "keepalive":
|
||||||
// util.log({ keepalive: serial });
|
// util.log({ keepalive: serial });
|
||||||
mcache[serial]?.keepalive();
|
mcache[serial]?.keepalive();
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ gapp.overlay(root, {
|
||||||
// add common element types
|
// add common element types
|
||||||
[
|
[
|
||||||
"a", "i", "hr", "div", "pre", "code", "span", "label", "input",
|
"a", "i", "hr", "div", "pre", "code", "span", "label", "input",
|
||||||
"button", "svg", "textarea", "select", "option"
|
"button", "svg", "textarea", "select", "option", "img", "canvas"
|
||||||
].forEach(type => {
|
].forEach(type => {
|
||||||
h[type] = (attr, innr) => {
|
h[type] = (attr, innr) => {
|
||||||
return h.el(type, attr, innr);
|
return h.el(type, attr, innr);
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,11 @@ details[open] > summary::after {
|
||||||
transform: rotate(90deg);
|
transform: rotate(90deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.video {
|
||||||
|
border: 1px solid #888;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
/* container for entire page / app */
|
/* container for entire page / app */
|
||||||
#app {
|
#app {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue