feat: add mqtt screenshot image #133

This commit is contained in:
leukipp
2025-11-25 17:12:43 +01:00
parent 1a79ffd855
commit 61628b1864
2 changed files with 89 additions and 7 deletions
+31
View File
@@ -94,6 +94,7 @@ const init = async () => {
initLastActive();
// Init client diagnostic
initScreenshot();
initHeartbeat();
initErrors();
initVersion();
@@ -116,6 +117,7 @@ const init = async () => {
updateDisplay();
updateLastActive();
});
EVENTS.on("updateScreenshot", updateScreenshot);
EVENTS.on("consoleLog", updateErrors);
})
.on("error", (error) => {
@@ -452,10 +454,12 @@ const initDisplay = () => {
unique_id: `${INTEGRATION.node}_display`,
command_topic: `${root}/power/set`,
state_topic: `${root}/power/state`,
supported_color_modes: ["onoff"],
icon: "mdi:monitor-shimmer",
platform: "light",
device: INTEGRATION.device,
...(HARDWARE.support.displayBrightness && {
supported_color_modes: ["brightness"],
brightness_command_topic: `${root}/brightness/set`,
brightness_state_topic: `${root}/brightness/state`,
brightness_scale: 100,
@@ -1050,6 +1054,33 @@ const updateLastActive = async () => {
publishAttributes("last_active", tracker);
};
/**
* Initializes the page screenshot.
*/
const initScreenshot = () => {
const root = `${INTEGRATION.root}/screenshot`;
const config = {
name: "Screenshot",
unique_id: `${INTEGRATION.node}_screenshot`,
image_topic: `${root}/state`,
image_encoding: "b64",
content_type: "image/png",
entity_category: "diagnostic",
icon: "mdi:image-area",
device: INTEGRATION.device,
};
publishConfig("image", config);
updateScreenshot();
};
/**
* Updates the page screenshot via the mqtt connection.
*/
const updateScreenshot = async () => {
const screenshot = WEBVIEW.tracker.screenshot;
publishState("screenshot", screenshot);
};
/**
* Initializes the heartbeat sensor.
*/
+58 -7
View File
@@ -29,6 +29,7 @@ global.WEBVIEW = global.WEBVIEW || {
widget: {
focused: null,
},
screenshot: null,
},
};
@@ -314,6 +315,11 @@ const updateView = () => {
WEBVIEW.status.webContents.send("text-content", { id: "title", content: title });
WEBVIEW.window.setTitle(title);
// Hide all other webviews and show only the active one
WEBVIEW.views.forEach((view, i) => {
view.setVisible(i === WEBVIEW.viewActive);
});
// Update stored theme and zoom
cookieStore("web-theme").then((value) => {
if (value && value !== WEBVIEW.theme.get()) {
@@ -326,9 +332,9 @@ const updateView = () => {
}
});
// Hide all other webviews and show only the active one
WEBVIEW.views.forEach((view, i) => {
view.setVisible(i === WEBVIEW.viewActive);
// Update webview screenshot
captureView(1000).then(() => {
EVENTS.emit("updateScreenshot");
});
EVENTS.emit("updatePage");
update();
@@ -711,6 +717,11 @@ const resizeView = () => {
theme: WEBVIEW.navigationTheme,
});
}
// Update webview screenshot
captureView(1000).then(() => {
EVENTS.emit("updateScreenshot");
});
};
/**
@@ -802,8 +813,8 @@ const windowEvents = async () => {
historyForward();
});
// Check for window touch events (1s)
setInterval(() => {
// Check for window touch events (every full 1s)
interval(() => {
if (APP.exiting) {
return;
}
@@ -1231,8 +1242,18 @@ const appEvents = async () => {
console.error(`${details.type} Process ${details.reason} (code ${details.exitCode}): ${name}`);
});
// Check latest release (2h)
setInterval(() => {
// Update latest screenshot (every full 1min)
interval(() => {
if (APP.exiting) {
return;
}
captureView(5000).then(() => {
EVENTS.emit("updateScreenshot");
});
}, 60 * 1000);
// Check latest release (every full 2h)
interval(() => {
if (APP.exiting) {
return;
}
@@ -1341,6 +1362,22 @@ const cookieStore = async (key, value, view = WEBVIEW.views[WEBVIEW.viewActive])
return !isNaN(Number(cookie.value)) ? Number(cookie.value) : cookie.value;
};
/**
* Captures a webview screenshot as a base64 image.
*
* @param {number} wait - The time to wait before capturing in milliseconds.
* @param {WebContentsView} view - The webview that captures the page.
* @returns {Promise<string|null>} The base64 image of the captured page or null if failed.
*/
const captureView = async (wait, view = WEBVIEW.views[WEBVIEW.viewActive]) => {
await new Promise((r) => setTimeout(r, wait));
const image = await view.webContents.capturePage();
const dataUrl = image.toDataURL();
const dataString = dataUrl.replace(/^data:image\/\w+;base64,/, "").trim();
WEBVIEW.tracker.screenshot = dataString || WEBVIEW.tracker.screenshot;
return WEBVIEW.tracker.screenshot;
};
/**
* Generates a html template for a spinning loader.
*
@@ -1439,6 +1476,20 @@ const errorHtml = (code, text, url, theme) => {
return `data:text/html;charset=utf-8,${encodeURIComponent(html)}`;
};
/**
* Interval function that runs at interval boundaries.
*
* @param {Function} callback - The function to execute.
* @param {number} ms - The interval time in milliseconds.
*/
const interval = (callback, ms) => {
const delay = ms - (Date.now() % ms);
setTimeout(() => {
callback();
setInterval(callback, ms);
}, delay);
};
module.exports = {
init,
update,