diff --git a/app.py b/app.py index 489905c..e5e130f 100644 --- a/app.py +++ b/app.py @@ -151,6 +151,7 @@ last_notified_errors = {} device_health = {} last_seen_reflector = {} network_mapping = {} +node_info = {} if os.path.exists(CACHE_FILE): try: @@ -300,6 +301,25 @@ def on_message(client, userdata, msg): except Exception as e: logger.error(f"Error parsing DMRGateway for {cid}: {e}") + + # --- MMDVMHOST INFO MANAGEMENT (FREQUENZE) --- + elif len(parts) >= 4 and parts[0] == 'data' and parts[2].lower() == 'mmdvmhost' and parts[3].lower() == 'info': + try: + cid = parts[1].lower() + data = json.loads(payload) + tx = data.get("TXFrequency", "0") + rx = data.get("RXFrequency", "0") + + # Funzione per formattare gli Hz in MHz (es. 430500000 -> 430.500 MHz) + def format_freq(f): + if str(f).isdigit() and int(f) > 0: + return f"{int(f)/1000000:.3f} MHz" + return str(f) + + node_info[cid] = {"tx": format_freq(tx), "rx": format_freq(rx)} + socketio.emit('dati_aggiornati') # <--- Aggiorna la UI in tempo reale + except Exception as e: + logger.error(f"Error parsing MMDVMHost info for {cid}: {e}") # --- OTHER GATEWAYS MANAGEMENT --- elif parts[0] in ['dmr-gateway', 'nxdn-gateway', 'ysf-gateway', 'p25-gateway', 'dstar-gateway']: @@ -430,7 +450,8 @@ def get_states(): "states": client_states, "telemetry": client_telemetry, "health": device_health, - "networks": network_mapping + "networks": network_mapping, + "info": node_info }) @app.route('/api/stats') diff --git a/templates/index.html b/templates/index.html index b1af1ef..a734a8a 100644 --- a/templates/index.html +++ b/templates/index.html @@ -46,7 +46,12 @@ /* Telemetria stile terminale */ .health-bar { display: flex; justify-content: space-between; font-size: 0.75rem; font-weight: 600; background: #010409; padding: 8px 12px; border-radius: 3px; margin-bottom: 15px; border: 1px solid var(--border-color); font-family: 'JetBrains Mono', monospace; } - + + /* Frequenze Radio */ + .freq-bar { display: flex; justify-content: space-around; font-size: 0.8rem; font-weight: 800; background: rgba(0,0,0,0.2); padding: 8px 10px; border-radius: 3px; margin-bottom: 15px; border: 1px dashed var(--border-color); font-family: 'JetBrains Mono', monospace; } + .freq-tx { color: #f87171; } /* Rosso per la TX */ + .freq-rx { color: #4ade80; } /* Verde per la RX */ + /* Display Stato */ .status-display { text-align: center; font-family: 'JetBrains Mono', monospace; font-size: 0.9rem; font-weight: 700; padding: 8px; border-radius: 3px; background: #010409; border: 1px solid var(--border-color); margin-bottom: 15px; color: var(--text-muted); } .card.online .status-display { color: var(--success); border-color: rgba(46, 160, 67, 0.4); } @@ -582,12 +587,16 @@ let showReboot = (role === 'admin'); return ` -