@@ -59,7 +59,7 @@ app.whenReady().then(async () => {
|
|||||||
global.ARGS = args;
|
global.ARGS = args;
|
||||||
|
|
||||||
// Show used arguments
|
// Show used arguments
|
||||||
console.log(`Arguments: ${JSON.stringify(global.ARGS, null, 2)}`);
|
console.log(`Arguments: ${JSON.stringify(global.ARGS, null, 2)}\n`);
|
||||||
|
|
||||||
// Chained init functions
|
// Chained init functions
|
||||||
const chained = [webview.init, hardware.init, integration.init];
|
const chained = [webview.init, hardware.init, integration.init];
|
||||||
|
|||||||
+55
-39
@@ -3,10 +3,11 @@ const fs = require("fs");
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const fsp = require("fs/promises");
|
const fsp = require("fs/promises");
|
||||||
const cpr = require("child_process");
|
const cpr = require("child_process");
|
||||||
|
const Events = require("events");
|
||||||
|
|
||||||
global.HARDWARE = global.HARDWARE || {
|
global.HARDWARE = global.HARDWARE || {
|
||||||
initialized: false,
|
initialized: false,
|
||||||
status: null,
|
events: new Events(),
|
||||||
session: {},
|
session: {},
|
||||||
support: {},
|
support: {},
|
||||||
battery: {
|
battery: {
|
||||||
@@ -24,11 +25,9 @@ global.HARDWARE = global.HARDWARE || {
|
|||||||
value: null,
|
value: null,
|
||||||
max: null,
|
max: null,
|
||||||
},
|
},
|
||||||
notifiers: [],
|
|
||||||
},
|
},
|
||||||
keyboard: {
|
keyboard: {
|
||||||
visible: null,
|
visible: null,
|
||||||
notifiers: [],
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,7 +38,11 @@ global.HARDWARE = global.HARDWARE || {
|
|||||||
*/
|
*/
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
if (!compatibleDevice()) {
|
if (!compatibleDevice()) {
|
||||||
console.warn("Device not supported");
|
console.warn("Operating system is not supported\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!sessionRights()) {
|
||||||
|
console.warn(`User "${sessionUser()}" is missing password-less sudo rights\n`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +58,6 @@ const init = async () => {
|
|||||||
HARDWARE.display.brightness.max = getDisplayBrightnessMax();
|
HARDWARE.display.brightness.max = getDisplayBrightnessMax();
|
||||||
HARDWARE.support.keyboardVisibility = processRuns("squeekboard");
|
HARDWARE.support.keyboardVisibility = processRuns("squeekboard");
|
||||||
HARDWARE.initialized = true;
|
HARDWARE.initialized = true;
|
||||||
HARDWARE.status = "Supported";
|
|
||||||
|
|
||||||
// Show supported features
|
// Show supported features
|
||||||
console.log(`Supported: ${JSON.stringify(HARDWARE.support, null, 2)}`);
|
console.log(`Supported: ${JSON.stringify(HARDWARE.support, null, 2)}`);
|
||||||
@@ -92,9 +94,7 @@ const init = async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
HARDWARE.keyboard.visibility = property.Visible === "true";
|
HARDWARE.keyboard.visibility = property.Visible === "true";
|
||||||
HARDWARE.keyboard.notifiers.forEach((notifier) => {
|
HARDWARE.events.emit("updateKeyboard");
|
||||||
notifier();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -118,9 +118,7 @@ const update = async () => {
|
|||||||
if (status !== HARDWARE.display.status.value) {
|
if (status !== HARDWARE.display.status.value) {
|
||||||
console.log("Update Display Status:", getDisplayStatus());
|
console.log("Update Display Status:", getDisplayStatus());
|
||||||
HARDWARE.display.status.value = status;
|
HARDWARE.display.status.value = status;
|
||||||
HARDWARE.display.notifiers.forEach((notifier) => {
|
HARDWARE.events.emit("updateDisplay");
|
||||||
notifier();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,9 +128,7 @@ const update = async () => {
|
|||||||
if (brightness !== HARDWARE.display.brightness.value) {
|
if (brightness !== HARDWARE.display.brightness.value) {
|
||||||
console.log("Update Display Brightness:", getDisplayBrightness());
|
console.log("Update Display Brightness:", getDisplayBrightness());
|
||||||
HARDWARE.display.brightness.value = brightness;
|
HARDWARE.display.brightness.value = brightness;
|
||||||
HARDWARE.display.notifiers.forEach((notifier) => {
|
HARDWARE.events.emit("updateDisplay");
|
||||||
notifier();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -150,6 +146,19 @@ const compatibleDevice = () => {
|
|||||||
return paths.every((path) => fs.existsSync(path));
|
return paths.every((path) => fs.existsSync(path));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies user rights to run sudo commands without a password.
|
||||||
|
*
|
||||||
|
* @returns {bool} Returns true if password-less sudo rights exists.
|
||||||
|
*/
|
||||||
|
const sessionRights = () => {
|
||||||
|
if (!commandExists("sudo")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const result = execSyncCommand("sudo", ["-n", "true"]);
|
||||||
|
return result !== null && !result.includes("password is required");
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the session user name using `os.userInfo()`.
|
* Gets the session user name using `os.userInfo()`.
|
||||||
*
|
*
|
||||||
@@ -178,15 +187,6 @@ const sessionType = () => {
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the session type for the logged in user.
|
|
||||||
*
|
|
||||||
* @returns {bool} Returns true if the session matches the type.
|
|
||||||
*/
|
|
||||||
const session = (type) => {
|
|
||||||
return HARDWARE.session.type === type;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the model name using `/sys/firmware/devicetree/base/model` or `/sys/class/dmi/id/product_name`.
|
* Gets the model name using `/sys/firmware/devicetree/base/model` or `/sys/class/dmi/id/product_name`.
|
||||||
*
|
*
|
||||||
@@ -379,18 +379,25 @@ const getDisplayStatus = () => {
|
|||||||
if (!HARDWARE.support.displayStatus) {
|
if (!HARDWARE.support.displayStatus) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (session("wayland") && commandExists("wlopm")) {
|
switch (HARDWARE.session.type) {
|
||||||
const status = execSyncCommand("wlopm", []);
|
case "wayland":
|
||||||
if (status !== null) {
|
if (commandExists("wlopm")) {
|
||||||
const out = status.split("\n")[0].split(" ");
|
const status = execSyncCommand("wlopm", []);
|
||||||
return out.pop().toUpperCase();
|
if (status !== null) {
|
||||||
}
|
const out = status.split("\n")[0].split(" ");
|
||||||
} else if (session("x11") && commandExists("xset")) {
|
return out.pop().toUpperCase();
|
||||||
const status = execSyncCommand("xset", ["-q"]);
|
}
|
||||||
if (status !== null) {
|
}
|
||||||
const on = /Monitor is On/.test(status);
|
break;
|
||||||
return on ? "ON" : "OFF";
|
case "x11":
|
||||||
}
|
if (commandExists("xset")) {
|
||||||
|
const status = execSyncCommand("xset", ["-q"]);
|
||||||
|
if (status !== null) {
|
||||||
|
const on = /Monitor is On/.test(status);
|
||||||
|
return on ? "ON" : "OFF";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
@@ -414,10 +421,19 @@ const setDisplayStatus = (status, callback = null) => {
|
|||||||
if (typeof callback === "function") callback(null, "Invalid status");
|
if (typeof callback === "function") callback(null, "Invalid status");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (session("wayland") && commandExists("wlopm")) {
|
switch (HARDWARE.session.type) {
|
||||||
execAsyncCommand("wlopm", [`--${status.toLowerCase()}`, "*"], callback);
|
case "wayland":
|
||||||
} else if (session("x11") && commandExists("xset")) {
|
if (commandExists("wlopm")) {
|
||||||
execAsyncCommand("xset", ["dpms", "force", status.toLowerCase()], callback);
|
execAsyncCommand("wlopm", [`--${status.toLowerCase()}`, "*"], callback);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "x11":
|
||||||
|
if (commandExists("xset")) {
|
||||||
|
execAsyncCommand("xset", ["dpms", "force", status.toLowerCase()], callback);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (typeof callback === "function") callback(null, "Invalid session type");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+12
-15
@@ -1,10 +1,11 @@
|
|||||||
const mqtt = require("mqtt");
|
const mqtt = require("mqtt");
|
||||||
const hardware = require("./hardware");
|
const hardware = require("./hardware");
|
||||||
const { app } = require("electron");
|
const { app } = require("electron");
|
||||||
|
const Events = require("events");
|
||||||
|
|
||||||
global.INTEGRATION = global.INTEGRATION || {
|
global.INTEGRATION = global.INTEGRATION || {
|
||||||
initialized: false,
|
initialized: false,
|
||||||
status: null,
|
events: new Events(),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,20 +87,14 @@ const init = async () => {
|
|||||||
|
|
||||||
// Integration initialized
|
// Integration initialized
|
||||||
INTEGRATION.initialized = true;
|
INTEGRATION.initialized = true;
|
||||||
INTEGRATION.status = "Online";
|
|
||||||
})
|
})
|
||||||
.on("error", (error) => {
|
.on("error", (error) => {
|
||||||
console.error("MQTT:", error);
|
console.error("MQTT:", error);
|
||||||
INTEGRATION.status = "Offline";
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update sensor states from notifiers
|
// Update sensor states from events
|
||||||
HARDWARE.display.notifiers.push(() => {
|
HARDWARE.events.on("updateDisplay", updateDisplay);
|
||||||
updateDisplay();
|
HARDWARE.events.on("updateKeyboard", updateKeyboard);
|
||||||
});
|
|
||||||
HARDWARE.keyboard.notifiers.push(() => {
|
|
||||||
updateKeyboard();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update time sensors periodically (30s)
|
// Update time sensors periodically (30s)
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
@@ -250,7 +245,7 @@ const initRefresh = () => {
|
|||||||
if (topic === config.command_topic) {
|
if (topic === config.command_topic) {
|
||||||
console.log("Refreshing webview...");
|
console.log("Refreshing webview...");
|
||||||
hardware.setDisplayStatus("ON");
|
hardware.setDisplayStatus("ON");
|
||||||
WEBVIEW.views[WEBVIEW.viewActive].webContents.reloadIgnoringCache();
|
WEBVIEW.events.emit("reloadView");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.subscribe(config.command_topic);
|
.subscribe(config.command_topic);
|
||||||
@@ -441,6 +436,7 @@ const initPageNumber = () => {
|
|||||||
const number = parseInt(message, 10);
|
const number = parseInt(message, 10);
|
||||||
console.log("Set Page Number:", number);
|
console.log("Set Page Number:", number);
|
||||||
WEBVIEW.viewActive = number;
|
WEBVIEW.viewActive = number;
|
||||||
|
WEBVIEW.events.emit("updateView");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.subscribe(config.command_topic);
|
.subscribe(config.command_topic);
|
||||||
@@ -467,8 +463,8 @@ const initPageZoom = () => {
|
|||||||
state_topic: `${root}/state`,
|
state_topic: `${root}/state`,
|
||||||
value_template: "{{ value | int }}",
|
value_template: "{{ value | int }}",
|
||||||
mode: "slider",
|
mode: "slider",
|
||||||
min: 10,
|
min: 5,
|
||||||
max: 400,
|
max: 500,
|
||||||
unit_of_measurement: "%",
|
unit_of_measurement: "%",
|
||||||
icon: "mdi:magnify-plus-outline",
|
icon: "mdi:magnify-plus-outline",
|
||||||
device: INTEGRATION.device,
|
device: INTEGRATION.device,
|
||||||
@@ -478,7 +474,8 @@ const initPageZoom = () => {
|
|||||||
if (topic === config.command_topic) {
|
if (topic === config.command_topic) {
|
||||||
const zoom = parseInt(message, 10);
|
const zoom = parseInt(message, 10);
|
||||||
console.log("Set Page Zoom:", zoom);
|
console.log("Set Page Zoom:", zoom);
|
||||||
WEBVIEW.viewZoom = zoom / 100.0;
|
WEBVIEW.views[WEBVIEW.viewActive].webContents.setZoomFactor(zoom / 100.0);
|
||||||
|
WEBVIEW.events.emit("updateView");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.subscribe(config.command_topic);
|
.subscribe(config.command_topic);
|
||||||
@@ -489,7 +486,7 @@ const initPageZoom = () => {
|
|||||||
* Updates the page zoom via the mqtt connection.
|
* Updates the page zoom via the mqtt connection.
|
||||||
*/
|
*/
|
||||||
const updatePageZoom = () => {
|
const updatePageZoom = () => {
|
||||||
const pageZoom = Math.round(WEBVIEW.viewZoom * 100.0);
|
const pageZoom = Math.round(WEBVIEW.views[WEBVIEW.viewActive].webContents.getZoomFactor() * 100.0);
|
||||||
publishState("page_zoom", pageZoom);
|
publishState("page_zoom", pageZoom);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+10
-32
@@ -2,9 +2,11 @@ const path = require("path");
|
|||||||
const hardware = require("./hardware");
|
const hardware = require("./hardware");
|
||||||
const integration = require("./integration");
|
const integration = require("./integration");
|
||||||
const { app, screen, nativeTheme, ipcMain, session, BaseWindow, WebContentsView } = require("electron");
|
const { app, screen, nativeTheme, ipcMain, session, BaseWindow, WebContentsView } = require("electron");
|
||||||
|
const Events = require("events");
|
||||||
|
|
||||||
global.WEBVIEW = global.WEBVIEW || {
|
global.WEBVIEW = global.WEBVIEW || {
|
||||||
initialized: false,
|
initialized: false,
|
||||||
|
events: new Events(),
|
||||||
status: null,
|
status: null,
|
||||||
locked: false,
|
locked: false,
|
||||||
pointer: {
|
pointer: {
|
||||||
@@ -37,31 +39,17 @@ const init = async () => {
|
|||||||
const urls = [loaderHtml(40, 1.0, theme)].concat(ARGS.web_url);
|
const urls = [loaderHtml(40, 1.0, theme)].concat(ARGS.web_url);
|
||||||
|
|
||||||
// Init global properties
|
// Init global properties
|
||||||
|
WEBVIEW.viewActive = 0;
|
||||||
WEBVIEW.viewUrls = urls;
|
WEBVIEW.viewUrls = urls;
|
||||||
Object.defineProperty(WEBVIEW, "viewZoom", {
|
WEBVIEW.viewZoom = zoom;
|
||||||
get() {
|
|
||||||
return this._viewZoom || zoom;
|
|
||||||
},
|
|
||||||
set(v) {
|
|
||||||
this._viewZoom = Math.max(0, v);
|
|
||||||
updateZoom();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
WEBVIEW.viewTheme = theme;
|
WEBVIEW.viewTheme = theme;
|
||||||
WEBVIEW.widgetTheme = theme;
|
WEBVIEW.widgetTheme = theme;
|
||||||
WEBVIEW.widgetEnabled = widget;
|
WEBVIEW.widgetEnabled = widget;
|
||||||
WEBVIEW.navigationTheme = theme;
|
WEBVIEW.navigationTheme = theme;
|
||||||
WEBVIEW.navigationEnabled = widget;
|
WEBVIEW.navigationEnabled = widget;
|
||||||
nativeTheme.themeSource = WEBVIEW.viewTheme;
|
nativeTheme.themeSource = WEBVIEW.viewTheme;
|
||||||
Object.defineProperty(WEBVIEW, "viewActive", {
|
WEBVIEW.events.on("reloadView", reloadView);
|
||||||
get() {
|
WEBVIEW.events.on("updateView", updateView);
|
||||||
return this._viewActive || 0;
|
|
||||||
},
|
|
||||||
set(v) {
|
|
||||||
this._viewActive = Math.max(0, Math.min(v, WEBVIEW.viewUrls.length - 1));
|
|
||||||
updateView();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Init global root window
|
// Init global root window
|
||||||
WEBVIEW.window = new BaseWindow({
|
WEBVIEW.window = new BaseWindow({
|
||||||
@@ -263,6 +251,7 @@ const historyForward = () => {
|
|||||||
*/
|
*/
|
||||||
const nextView = () => {
|
const nextView = () => {
|
||||||
WEBVIEW.viewActive = (WEBVIEW.viewActive + 1) % WEBVIEW.views.length || 1;
|
WEBVIEW.viewActive = (WEBVIEW.viewActive + 1) % WEBVIEW.views.length || 1;
|
||||||
|
updateView();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -297,7 +286,6 @@ const resizeView = () => {
|
|||||||
width: window.width,
|
width: window.width,
|
||||||
height: window.height - navigation.height,
|
height: window.height - navigation.height,
|
||||||
});
|
});
|
||||||
view.webContents.setZoomFactor(WEBVIEW.viewZoom);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update widget size
|
// Update widget size
|
||||||
@@ -323,17 +311,6 @@ const resizeView = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates zoom level for all webviews.
|
|
||||||
*/
|
|
||||||
const updateZoom = () => {
|
|
||||||
// And apply to all views
|
|
||||||
WEBVIEW.views.forEach((view) => {
|
|
||||||
view.webContents.setZoomFactor(WEBVIEW.viewZoom);
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register window events and handler.
|
* Register window events and handler.
|
||||||
*/
|
*/
|
||||||
@@ -351,7 +328,7 @@ const windowEvents = () => {
|
|||||||
}
|
}
|
||||||
WEBVIEW.locked = false;
|
WEBVIEW.locked = false;
|
||||||
});
|
});
|
||||||
HARDWARE.display.notifiers.push(() => {
|
HARDWARE.events.on("updateDisplay", () => {
|
||||||
WEBVIEW.locked = hardware.getDisplayStatus() === "OFF";
|
WEBVIEW.locked = hardware.getDisplayStatus() === "OFF";
|
||||||
if (WEBVIEW.locked) {
|
if (WEBVIEW.locked) {
|
||||||
WEBVIEW.window.blur();
|
WEBVIEW.window.blur();
|
||||||
@@ -577,8 +554,9 @@ const domEvents = () => {
|
|||||||
return { action: "deny" };
|
return { action: "deny" };
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove webview scrollbar
|
// Update webview layout
|
||||||
view.webContents.on("dom-ready", () => {
|
view.webContents.on("dom-ready", () => {
|
||||||
|
view.webContents.setZoomFactor(WEBVIEW.viewZoom);
|
||||||
view.webContents.insertCSS("::-webkit-scrollbar { display: none; }");
|
view.webContents.insertCSS("::-webkit-scrollbar { display: none; }");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user