🖥️ Pi System Monitor – JavaScript
// V26.06.008
// Linux Pi-System Monitor
// Bessere Fehlerbehandlung und optimierter Code verwendet nun async/await.
// Das Skript überwacht die Spannungswarnung, CPU-Temperatur und die Uptime eines Raspberry Pi-Systems,
// und speichert diese Informationen regelmäßig in ioBroker-Datenpunkten,
// wobei die Temperatur auf eine Stelle und die Uptime auf zwei Stellen hinter dem Komma begrenzt wird.
// Node exec
const { exec } = require("child_process");
const util = require("util");
const execAsync = util.promisify(exec);
// Pfad für die Datenpunkte
const dataPath = "javascript.0.linux-pi-system";
// --------------------------------------------------
// State erstellen / updaten
// --------------------------------------------------
async function updateOrCreateState(path, value, type = null) {
try {
const exists = await existsStateAsync(path);
if (!exists) {
const finalType = type || (
value === null || value === undefined ? "mixed" :
Array.isArray(value) ? "array" :
typeof value
);
await createStateAsync(path, value, {
name: path.split('.').pop(),
role: finalType === "string" ? "text" : "value",
type: finalType,
read: true,
write: false
});
}
await setStateAsync(path, value, true);
} catch (err) {
log(`Fehler (${path}): ${err.message || err}`, "error");
}
}
// --------------------------------------------------
// CPU Temperatur
// --------------------------------------------------
async function saveCPUTemperature() {
try {
const { stdout } = await execAsync("cat /sys/class/thermal/thermal_zone0/temp");
const raw = stdout.trim();
const milli = Number(raw);
if (isNaN(milli)) throw new Error(`ungültiger Wert: ${raw}`);
const celsius = Number((milli / 1000).toFixed(1));
await updateOrCreateState(`${dataPath}.temperature`, celsius);
} catch (error) {
log(`CPU Temp Fehler: ${error.message || error}`, "error");
}
}
// --------------------------------------------------
// Uptime
// --------------------------------------------------
async function saveUptime() {
try {
const { stdout } = await execAsync("cat /proc/uptime");
const raw = stdout.trim().split(' ')[0];
const seconds = Number(raw);
if (isNaN(seconds)) throw new Error(`ungültiger Wert: ${raw}`);
const hours = Number((seconds / 3600).toFixed(2));
await updateOrCreateState(`${dataPath}.uptime`, hours);
} catch (error) {
log(`Uptime Fehler: ${error.message || error}`, "error");
}
}
// --------------------------------------------------
// Voltage / Throttling Check via vcgencmd
//
// Voraussetzung:
// - Der ausführende Benutzer muss Mitglied der Gruppe "video" sein,
// damit Zugriff auf /dev/vcio (vcgencmd) ohne sudo möglich ist.
//
// Hintergrund:
// - vcgencmd greift auf /dev/vcio zu.
// - Ohne passende Rechte schlägt der Befehl fehl (Permission denied).
// - Betrifft u.a. ioBroker, EOS und iob diag.
//
// Einrichtung für ioBroker:
// sudo usermod -aG video iobroker
//
// Optional (für eigenen Login-User):
// sudo usermod -aG video $USER
//
// Hinweis:
// - Danach ist ein Logout, Neustart oder Service-Neustart erforderlich,
// damit die Gruppenänderung wirksam wird.
//
// Prüfung:
// - Alle Mitglieder der Gruppe anzeigen:
// getent group video
// --------------------------------------------------
async function checkVoltageWarning() {
try {
const { stdout } = await execAsync("vcgencmd get_throttled");
const raw = stdout.trim();
// Beispiel: throttled=0x50000
const match = raw.match(/0x([0-9a-fA-F]+)/);
if (!match) throw new Error(`Ungültige Antwort: ${raw}`);
const value = parseInt(match[1], 16);
// Bitmasken laut Raspberry Pi Doku
const UNDER_VOLTAGE_NOW = 0x1;
const THROTTLED_NOW = 0x2;
const FREQ_CAPPED_NOW = 0x4;
const UNDER_VOLTAGE_PAST = 0x10000;
const THROTTLED_PAST = 0x20000;
const FREQ_CAPPED_PAST = 0x40000;
let status = [];
// Aktuelle Probleme
if (value & UNDER_VOLTAGE_NOW) status.push("⚠️ Under-voltage NOW");
if (value & THROTTLED_NOW) status.push("🔥 Throttled NOW");
if (value & FREQ_CAPPED_NOW) status.push("📉 Frequency capped NOW");
// Historische Probleme
if (value & UNDER_VOLTAGE_PAST) status.push("Under-voltage occurred");
if (value & THROTTLED_PAST) status.push("Throttling occurred");
if (value & FREQ_CAPPED_PAST) status.push("Frequency capped occurred");
const text = status.length ? status.join(" | ") : "✅ Voltage OK";
await updateOrCreateState(`${dataPath}.powerWarning`, text);
// Optional: Rohwert extra speichern (für Debug / VIS)
await updateOrCreateState(`${dataPath}.powerWarningRaw`, value);
} catch (error) {
await updateOrCreateState(`${dataPath}.powerWarning`, "ERROR");
log(`Voltage Fehler: ${error.message || error}`, "error");
}
}
// --------------------------------------------------
// INITIALER LAUF
// --------------------------------------------------
(async () => {
await saveCPUTemperature();
await saveUptime();
await checkVoltageWarning();
})();
// --------------------------------------------------
// ZEITPLAN
// --------------------------------------------------
schedule("*/5 * * * *", saveCPUTemperature); // alle 5 Minuten
schedule("*/10 * * * *", saveUptime); // alle 10 Minuten
schedule("*/1 * * * *", checkVoltageWarning); // jede Minute