feat: implement dynamic Callsign and Radio ID from MQTT General topic

- Node names and IDs are now automatically updated using MMDVMHost/General data
- Added fallback to clients.json values if node is offline or data is missing
- Improved UI with dynamic badge coloring and high-contrast text for IDs
This commit is contained in:
2026-04-26 23:37:43 +02:00
parent 2685ee0212
commit 0a7f7f718e
2 changed files with 41 additions and 4 deletions
+18 -1
View File
@@ -152,6 +152,7 @@ device_health = {}
last_seen_reflector = {} last_seen_reflector = {}
network_mapping = {} network_mapping = {}
node_info = {} node_info = {}
node_general = {}
if os.path.exists(CACHE_FILE): if os.path.exists(CACHE_FILE):
try: try:
@@ -302,6 +303,7 @@ def on_message(client, userdata, msg):
except Exception as e: except Exception as e:
logger.error(f"Error parsing DMRGateway for {cid}: {e}") logger.error(f"Error parsing DMRGateway for {cid}: {e}")
# --- MMDVMHOST INFO MANAGEMENT (FREQUENZE & LOCATION) ---
elif len(parts) >= 4 and parts[0] == 'data' and parts[2].lower() == 'mmdvmhost' and parts[3].lower() == 'info': elif len(parts) >= 4 and parts[0] == 'data' and parts[2].lower() == 'mmdvmhost' and parts[3].lower() == 'info':
try: try:
cid = parts[1].lower() cid = parts[1].lower()
@@ -332,6 +334,20 @@ def on_message(client, userdata, msg):
except Exception as e: except Exception as e:
logger.error(f"Error parsing MMDVMHost info for {cid}: {e}") logger.error(f"Error parsing MMDVMHost info for {cid}: {e}")
# --- MMDVMHOST GENERAL MANAGEMENT (CALLSIGN & ID) ---
elif len(parts) >= 4 and parts[0] == 'data' and parts[2].lower() == 'mmdvmhost' and parts[3].lower() == 'general':
try:
cid = parts[1].lower()
data = json.loads(payload)
callsign = data.get("Callsign", "")
radio_id = data.get("Id", "")
if callsign:
node_general[cid] = {"callsign": callsign, "radio_id": radio_id}
socketio.emit('dati_aggiornati')
except Exception as e:
logger.error(f"Error parsing MMDVMHost general for {cid}: {e}")
# --- OTHER GATEWAYS MANAGEMENT --- # --- OTHER GATEWAYS MANAGEMENT ---
elif parts[0] in ['dmr-gateway', 'nxdn-gateway', 'ysf-gateway', 'p25-gateway', 'dstar-gateway']: elif parts[0] in ['dmr-gateway', 'nxdn-gateway', 'ysf-gateway', 'p25-gateway', 'dstar-gateway']:
data = json.loads(payload) data = json.loads(payload)
@@ -462,7 +478,8 @@ def get_states():
"telemetry": client_telemetry, "telemetry": client_telemetry,
"health": device_health, "health": device_health,
"networks": network_mapping, "networks": network_mapping,
"info": node_info "info": node_info,
"general": node_general
}) })
@app.route('/api/stats') @app.route('/api/stats')
+22 -2
View File
@@ -670,10 +670,31 @@
const statusDiv = document.getElementById(`status-${c.id}`); const cardDiv = document.getElementById(`card-${c.id}`); const statusDiv = document.getElementById(`status-${c.id}`); const cardDiv = document.getElementById(`card-${c.id}`);
if (!statusDiv || !cardDiv) return; if (!statusDiv || !cardDiv) return;
// 1. PRIMA CALCOLIAMO SE È ONLINE
let stateValue = data.states[c.id.toLowerCase()] || data.states[c.id] || "OFFLINE"; let stateValue = data.states[c.id.toLowerCase()] || data.states[c.id] || "OFFLINE";
stateValue = String(stateValue).trim().toUpperCase(); statusDiv.innerText = stateValue; stateValue = String(stateValue).trim().toUpperCase(); statusDiv.innerText = stateValue;
const isOnline = !stateValue.includes("OFF") && stateValue !== ""; const isOnline = !stateValue.includes("OFF") && stateValue !== "";
// 2. POI AGGIORNIAMO NOME E ID DINAMICI
let genObj = data.general && data.general[c.id.toLowerCase()];
const nameSpan = cardDiv.querySelector('.client-name');
const idBadge = cardDiv.querySelector('.badge-id');
if (genObj && isOnline) {
nameSpan.innerText = genObj.callsign;
idBadge.innerText = `ID: ${genObj.radio_id}`;
idBadge.style.background = "var(--primary)";
idBadge.style.color = "#ffffff"; // <--- FORZA IL TESTO BIANCO
idBadge.style.borderColor = "var(--primary)"; // <--- ADATTA IL BORDO
} else {
nameSpan.innerText = c.name;
idBadge.innerText = `ID: ${c.id.toUpperCase()}`;
idBadge.style.background = "rgba(255,255,255,0.1)";
idBadge.style.color = "var(--text-muted)"; // <--- RIPRISTINA IL GRIGIO
idBadge.style.borderColor = "var(--border-color)";
}
// 3. TELEMETRIA E STATO (PULITO DAI DUPLICATI)
let telemetryObj = data.telemetry[c.id.toLowerCase()] || data.telemetry[c.id] || { ts1: "...", ts2: "...", alt: "", idle: true }; let telemetryObj = data.telemetry[c.id.toLowerCase()] || data.telemetry[c.id] || { ts1: "...", ts2: "...", alt: "", idle: true };
if (typeof telemetryObj === 'string') telemetryObj = { ts1: telemetryObj, ts2: "...", alt: "", idle: true }; if (typeof telemetryObj === 'string') telemetryObj = { ts1: telemetryObj, ts2: "...", alt: "", idle: true };
@@ -717,7 +738,6 @@
} }
let healthObj = data.health && data.health[c.id.toLowerCase()]; let healthObj = data.health && data.health[c.id.toLowerCase()];
// --- INIZIO BLOCCO INFO AGGIUNTIVE (FREQ + LOC) ---
let infoObj = data.info && data.info[c.id.toLowerCase()]; let infoObj = data.info && data.info[c.id.toLowerCase()];
const freqContainer = document.getElementById(`freq-container-${c.id}`); const freqContainer = document.getElementById(`freq-container-${c.id}`);
const freqTx = document.getElementById(`freq-tx-${c.id}`); const freqTx = document.getElementById(`freq-tx-${c.id}`);
@@ -743,7 +763,7 @@
if (freqContainer) freqContainer.style.display = 'none'; if (freqContainer) freqContainer.style.display = 'none';
if (metaContainer) metaContainer.style.display = 'none'; if (metaContainer) metaContainer.style.display = 'none';
} }
// --- FINE BLOCCO INFO AGGIUNTIVE ---
const healthContainer = document.getElementById(`health-${c.id}`); const cpuSpan = document.getElementById(`cpu-${c.id}`); const tempSpan = document.getElementById(`temp-${c.id}`); const ramSpan = document.getElementById(`ram-${c.id}`); const diskSpan = document.getElementById(`disk-${c.id}`); const healthContainer = document.getElementById(`health-${c.id}`); const cpuSpan = document.getElementById(`cpu-${c.id}`); const tempSpan = document.getElementById(`temp-${c.id}`); const ramSpan = document.getElementById(`ram-${c.id}`); const diskSpan = document.getElementById(`disk-${c.id}`);
if (healthObj && isOnline) { if (healthObj && isOnline) {