diff --git a/DOCUMENTACION.md b/DOCUMENTACION.md index a14d198..c14e4ce 100644 --- a/DOCUMENTACION.md +++ b/DOCUMENTACION.md @@ -275,10 +275,18 @@ Si una RPi tiene un sensor externo de temperatura del rack, Node-RED puede escri } ``` -En el configurador, coloca la ruta en el dispositivo que tiene el sensor: +En el configurador, usa la seccion general `Temperatura Top-Rack`: ```text -/home/yamaray/monitor-rpi/rack-temp.json +RPi origen: RPi - MeshCore · 192.168.0.60 +Path JSON temperatura: /home/yamaray/monitor-rpi/rack-temp.json +``` + +La configuracion se guarda como: + +```text +rackTemperatureSourceDeviceId: rpi3-meshcore +rackTemperatureFile: /home/yamaray/monitor-rpi/rack-temp.json ``` El backend lee ese archivo durante el mismo scan SSH y muestra el valor como `Temperatura Top-Rack` en la primera linea del dashboard. diff --git a/config.example.json b/config.example.json index 86a8d8f..785e6b1 100644 --- a/config.example.json +++ b/config.example.json @@ -5,6 +5,8 @@ "sshTimeoutSeconds": 8, "sshPrivateKeyPath": "/ssh/monitor_rpi_ed25519", "sshPublicKeyPath": "/ssh/monitor_rpi_ed25519.pub", + "rackTemperatureSourceDeviceId": "rpi3-meshcore", + "rackTemperatureFile": "/home/yamaray/monitor-rpi/rack-temp.json", "temperatureThresholdsC": { "warning": 60, "hot": 70, @@ -41,7 +43,6 @@ "password": "", "authMethod": "password", "privateKeyPath": "", - "rackTemperatureFile": "", "role": "Home Assistant Local", "port": 22, "id": "rpi5-ha-main", @@ -56,7 +57,6 @@ "password": "", "authMethod": "password", "privateKeyPath": "", - "rackTemperatureFile": "", "role": "Home Assistant Cabaña", "port": 22, "id": "rpi4-ha-second", @@ -71,7 +71,6 @@ "password": "", "authMethod": "password", "privateKeyPath": "", - "rackTemperatureFile": "", "role": "Docker", "port": 22, "id": "rpi5-jose-docker", @@ -86,7 +85,6 @@ "password": "", "authMethod": "password", "privateKeyPath": "", - "rackTemperatureFile": "", "role": "Docker - Daniel", "port": 22, "id": "rpi5-dani-docker", @@ -101,7 +99,6 @@ "password": "", "authMethod": "password", "privateKeyPath": "", - "rackTemperatureFile": "", "role": "nginx - wireguard ", "port": 22, "id": "rpi3-nginx", @@ -116,7 +113,6 @@ "password": "", "authMethod": "password", "privateKeyPath": "", - "rackTemperatureFile": "", "role": "MQTT - varios", "port": 22, "id": "rpi3-pi3home", @@ -131,7 +127,6 @@ "password": "", "authMethod": "password", "privateKeyPath": "", - "rackTemperatureFile": "/home/yamaray/monitor-rpi/rack-temp.json", "role": "Meshcore-Interface", "port": 22, "id": "rpi3-meshcore", @@ -146,7 +141,6 @@ "password": "", "authMethod": "password", "privateKeyPath": "", - "rackTemperatureFile": "", "role": "Reserva", "port": 22, "id": "rpi3-node-3", diff --git a/config.html b/config.html index e44602c..e1a3ca3 100644 --- a/config.html +++ b/config.html @@ -233,7 +233,6 @@ .field-role { grid-column: span 3; } .field-model { grid-column: span 2; } .field-location { grid-column: span 2; } - .field-rack-temp { grid-column: span 4; } .field-host { grid-column: span 3; } .field-port { grid-column: span 1; } .field-username { grid-column: span 2; } @@ -446,7 +445,7 @@ @media (max-width: 1180px) { .device { grid-template-columns: repeat(6, minmax(0, 1fr)); align-items: start; } - .field-name, .field-role, .field-host, .password-credential, .field-rack-temp { grid-column: span 3; } + .field-name, .field-role, .field-host, .password-credential { grid-column: span 3; } .field-id, .field-model, .field-location, .field-username, .field-auth, .field-active, .field-delete { grid-column: span 2; } .field-port { grid-column: span 1; } .credential-note { grid-column: span 4; } @@ -461,7 +460,7 @@ .top-grid, .device { grid-template-columns: 1fr; } .field-active, .field-name, .field-id, .field-role, .field-model, .field-location, .field-host, .field-port, .field-username, .field-auth, .password-credential, - .credential-note, .field-rack-temp, .field-delete { grid-column: span 1; } + .credential-note, .field-delete { grid-column: span 1; } .threshold-row { grid-template-columns: 1fr; } } @@ -503,6 +502,17 @@ +
En el configurador, en ese dispositivo, usa Archivo temp rack con una ruta como:
/home/yamaray/monitor-rpi/rack-temp.json
+ En el configurador, usa la seccion general Temperatura Top-Rack:
RPi origen: RPi - MeshCore · 192.168.0.60
+Path JSON temperatura: /home/yamaray/monitor-rpi/rack-temp.json
cd /home/yamaray/docker/monitorRPi
diff --git a/server.js b/server.js
index 78ec852..7a6e163 100644
--- a/server.js
+++ b/server.js
@@ -49,6 +49,7 @@ async function readConfig() {
parsed.temperatureThresholdsC = normalizeTemperatureThresholds(parsed.temperatureThresholdsC);
parsed.metricThresholdsPercent = normalizeMetricThresholds(parsed.metricThresholdsPercent);
parsed.devices = Array.isArray(parsed.devices) ? parsed.devices.map(normalizeDeviceConfig) : [];
+ normalizeRackTemperatureConfig(parsed);
return parsed;
}
@@ -65,6 +66,7 @@ async function writeConfig(nextConfig) {
metricThresholdsPercent: normalizeMetricThresholds(nextConfig.metricThresholdsPercent),
devices: Array.isArray(nextConfig.devices) ? nextConfig.devices.map(normalizeDeviceConfig) : []
};
+ normalizeRackTemperatureConfig(normalized);
await fs.mkdir(path.dirname(CONFIG_PATH), { recursive: true });
await fs.writeFile(CONFIG_PATH, JSON.stringify(normalized, null, 2) + "\n", "utf8");
config = normalized;
@@ -80,6 +82,19 @@ function normalizeDeviceConfig(device = {}) {
};
}
+function normalizeRackTemperatureConfig(nextConfig) {
+ nextConfig.rackTemperatureSourceDeviceId = String(nextConfig.rackTemperatureSourceDeviceId || "");
+ nextConfig.rackTemperatureFile = String(nextConfig.rackTemperatureFile || "");
+
+ if (!nextConfig.rackTemperatureSourceDeviceId || !nextConfig.rackTemperatureFile) {
+ const legacyDevice = nextConfig.devices.find((device) => device.rackTemperatureFile);
+ if (legacyDevice) {
+ nextConfig.rackTemperatureSourceDeviceId = legacyDevice.id || "";
+ nextConfig.rackTemperatureFile = legacyDevice.rackTemperatureFile || "";
+ }
+ }
+}
+
function classifyTemp(tempC) {
const thresholds = config.temperatureThresholdsC || {};
if (tempC >= (thresholds.critical || 80)) return "critical";
@@ -130,6 +145,7 @@ function mockMetrics(device) {
const temp = seededNumber(`${device.id}:temp`, baseTemp - 4, baseTemp + 18);
const dockerRole = /docker|gitea|nginx/i.test(device.role || "");
const haRole = /home assistant/i.test(device.role || "");
+ const rackTemperatureFile = rackTemperatureFileForDevice(device);
return {
cpuTempC: Number(temp.toFixed(1)),
@@ -166,13 +182,13 @@ function mockMetrics(device) {
rxBytesPerSecond: Math.round(seededNumber(`${device.id}:rx`, 1200, 95000)),
txBytesPerSecond: Math.round(seededNumber(`${device.id}:tx`, 700, 55000))
},
- rackTemperature: device.rackTemperatureFile
+ rackTemperature: rackTemperatureFile
? {
temperatureC: Number(seededNumber(`${device.id}:rack-temp`, 25, 37).toFixed(1)),
sensor: "rack-top",
updatedAt: new Date().toISOString(),
ageSeconds: 0,
- sourceFile: device.rackTemperatureFile
+ sourceFile: rackTemperatureFile
}
: null
};
@@ -340,6 +356,13 @@ function shellQuote(value) {
return `'${String(value || "").replace(/'/g, `'\\''`)}'`;
}
+function rackTemperatureFileForDevice(device) {
+ const sourceId = config.rackTemperatureSourceDeviceId || "";
+ const sourceMatches = sourceId && [device.id, device.host, device.name].includes(sourceId);
+ if (sourceMatches) return config.rackTemperatureFile || "";
+ return device.rackTemperatureFile || "";
+}
+
function networkRates(device, values) {
const sampleKey = device.id || `${device.host}:${device.port || 22}`;
const now = Date.now();
@@ -470,7 +493,8 @@ async function sshMetrics(device) {
];
}
- const remoteScript = `RACK_TEMPERATURE_FILE=${shellQuote(device.rackTemperatureFile || "")}\n${REMOTE_METRICS_SCRIPT}`;
+ const rackTemperatureFile = rackTemperatureFileForDevice(device);
+ const remoteScript = `RACK_TEMPERATURE_FILE=${shellQuote(rackTemperatureFile)}\n${REMOTE_METRICS_SCRIPT}`;
const { stdout } = await runCommandWithInput(command, args, remoteScript, timeoutMs);
const values = parseKeyValueOutput(stdout);
const temp = toNumber(values.cpuTempC, 0);
@@ -511,7 +535,7 @@ async function sshMetrics(device) {
sensor: values.rackTemperatureSensor || "",
updatedAt: values.rackTemperatureUpdatedAt || "",
ageSeconds: toNumber(values.rackTemperatureAgeSeconds, null),
- sourceFile: device.rackTemperatureFile || ""
+ sourceFile: rackTemperatureFile
}
: null
};