Compare commits
3 Commits
bb697750b7
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5fe69b5846 | |||
| d4b901410e | |||
| 5f41744d93 |
@@ -38,6 +38,8 @@ The ecosystem consists of three main parts:
|
|||||||
* Run: `python3 app.py`
|
* Run: `python3 app.py`
|
||||||
|
|
||||||
#### 🔑 Generating VAPID Keys (Push Notifications)
|
#### 🔑 Generating VAPID Keys (Push Notifications)
|
||||||
|
> ⚠️ **IMPORTANT:** Web Push Notifications strictly require the dashboard to be accessed via a secure **HTTPS** connection (or localhost). Modern browsers will block push features over standard HTTP.
|
||||||
|
|
||||||
To enable Web Push Notifications, you must generate a unique VAPID key pair for your server.
|
To enable Web Push Notifications, you must generate a unique VAPID key pair for your server.
|
||||||
1. Go to a free online generator like [vapidkeys.com](https://vapidkeys.com/).
|
1. Go to a free online generator like [vapidkeys.com](https://vapidkeys.com/).
|
||||||
2. Generate the keys.
|
2. Generate the keys.
|
||||||
@@ -100,6 +102,8 @@ L'ecosistema si compone di tre parti principali:
|
|||||||
* Avvia: `python3 app.py`
|
* Avvia: `python3 app.py`
|
||||||
|
|
||||||
#### 🔑 Generare le chiavi VAPID (Notifiche Push)
|
#### 🔑 Generare le chiavi VAPID (Notifiche Push)
|
||||||
|
> ⚠️ **IMPORTANTE:** Le Notifiche Push Web richiedono tassativamente che la dashboard sia accessibile tramite una connessione sicura **HTTPS** (o localhost). I browser mobili e desktop bloccano le notifiche su connessioni HTTP non protette.
|
||||||
|
|
||||||
Per abilitare le Notifiche Push, devi generare una coppia di chiavi VAPID univoca per il tuo server.
|
Per abilitare le Notifiche Push, devi generare una coppia di chiavi VAPID univoca per il tuo server.
|
||||||
1. Vai su un generatore online gratuito come [vapidkeys.com](https://vapidkeys.com/).
|
1. Vai su un generatore online gratuito come [vapidkeys.com](https://vapidkeys.com/).
|
||||||
2. Genera la coppia di chiavi.
|
2. Genera la coppia di chiavi.
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ def init_db():
|
|||||||
c.execute("ALTER TABLE radio_logs ADD COLUMN protocol TEXT DEFAULT 'DMR'")
|
c.execute("ALTER TABLE radio_logs ADD COLUMN protocol TEXT DEFAULT 'DMR'")
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
c.execute("ALTER TABLE radio_logs ADD COLUMN source_ext TEXT DEFAULT ''")
|
||||||
|
except: pass
|
||||||
|
|
||||||
c.execute('''CREATE TABLE IF NOT EXISTS users
|
c.execute('''CREATE TABLE IF NOT EXISTS users
|
||||||
(id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE, password_hash TEXT,
|
(id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE, password_hash TEXT,
|
||||||
role TEXT, allowed_nodes TEXT)''')
|
role TEXT, allowed_nodes TEXT)''')
|
||||||
@@ -122,8 +126,8 @@ def save_cache(data):
|
|||||||
def save_to_sqlite(client_id, data, protocol="DMR"):
|
def save_to_sqlite(client_id, data, protocol="DMR"):
|
||||||
conn = sqlite3.connect(DB_PATH)
|
conn = sqlite3.connect(DB_PATH)
|
||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
c.execute("INSERT INTO radio_logs (timestamp, client_id, protocol, source_id, target, slot, duration, ber) VALUES (datetime('now', 'localtime'), ?, ?, ?, ?, ?, ?, ?)",
|
c.execute("INSERT INTO radio_logs (timestamp, client_id, protocol, source_id, target, slot, duration, ber, source_ext) VALUES (datetime('now', 'localtime'), ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
(client_id, protocol, str(data.get('source_id', '---')), str(data.get('destination_id', '---')), data.get('slot', 0), round(data.get('duration', 0), 1), round(data.get('ber', 0), 2)))
|
(client_id, protocol, str(data.get('source_id', '---')), str(data.get('destination_id', '---')), data.get('slot', 0), round(data.get('duration', 0), 1), round(data.get('ber', 0), 2), str(data.get('source_ext', ''))))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
socketio.emit('dati_aggiornati')
|
socketio.emit('dati_aggiornati')
|
||||||
@@ -345,13 +349,13 @@ def on_message(client, userdata, msg):
|
|||||||
else:
|
else:
|
||||||
target = current_target
|
target = current_target
|
||||||
|
|
||||||
active_calls[cid][k] = {'src': src, 'dst': target}
|
active_calls[cid][k] = {'src': src, 'dst': target, 'ext': str(p.get('source_ext', ''))}
|
||||||
client_telemetry[cid].update({"ts1":"","ts2":"","alt": f"{ico} {name}: {src} ➔ {target}"})
|
client_telemetry[cid].update({"ts1":"","ts2":"","alt": f"{ico} {name}: {src} ➔ {target}"})
|
||||||
socketio.emit('dati_aggiornati') # <--- WEBSOCKET
|
socketio.emit('dati_aggiornati') # <--- WEBSOCKET
|
||||||
|
|
||||||
elif act in ['end', 'lost']:
|
elif act in ['end', 'lost']:
|
||||||
info = active_calls[cid].get(k, {'src': '---', 'dst': '---'})
|
info = active_calls[cid].get(k, {'src': '---', 'dst': '---', 'ext': ''})
|
||||||
p.update({'source_id': info['src'], 'destination_id': info['dst']})
|
p.update({'source_id': info['src'], 'destination_id': info['dst'], 'source_ext': info['ext']})
|
||||||
save_to_sqlite(cid, p, protocol=name)
|
save_to_sqlite(cid, p, protocol=name)
|
||||||
client_telemetry[cid]["alt"] = f"{'✅' if act=='end' else '⚠️'} {name}: {info['src']}"
|
client_telemetry[cid]["alt"] = f"{'✅' if act=='end' else '⚠️'} {name}: {info['src']}"
|
||||||
save_cache(client_telemetry)
|
save_cache(client_telemetry)
|
||||||
@@ -381,7 +385,7 @@ def get_clients():
|
|||||||
def get_logs():
|
def get_logs():
|
||||||
conn = sqlite3.connect(DB_PATH, timeout=10)
|
conn = sqlite3.connect(DB_PATH, timeout=10)
|
||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
c.execute("SELECT timestamp, client_id, protocol, source_id, target, slot, duration, ber FROM radio_logs ORDER BY id DESC LIMIT 60")
|
c.execute("SELECT timestamp, client_id, protocol, source_id, target, slot, duration, ber, source_ext FROM radio_logs ORDER BY id DESC LIMIT 60")
|
||||||
logs = c.fetchall()
|
logs = c.fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
return jsonify(logs)
|
return jsonify(logs)
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 285 KiB After Width: | Height: | Size: 88 KiB |
+8
-10
@@ -4,14 +4,12 @@
|
|||||||
|
|
||||||
This guide describes the steps to install the Central
|
This guide describes the steps to install the Central
|
||||||
Dashboard and the Remote Agents on MMDVM nodes.
|
Dashboard and the Remote Agents on MMDVM nodes.
|
||||||
|
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
1. PRE-REQUISITES
|
1. PRE-REQUISITES
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
Ensure Python 3 is installed on all systems.
|
Ensure Python 3 is installed on all systems.
|
||||||
The necessary dependencies are listed in the
|
The necessary dependencies are listed in the
|
||||||
'requirements.txt' file.
|
'requirements.txt' file.
|
||||||
|
|
||||||
Install dependencies:
|
Install dependencies:
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
|
||||||
@@ -19,7 +17,6 @@ Install dependencies:
|
|||||||
2. SERVER SETUP (CENTRAL HUB)
|
2. SERVER SETUP (CENTRAL HUB)
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
The server handles the web interface and user permissions.
|
The server handles the web interface and user permissions.
|
||||||
|
|
||||||
Steps:
|
Steps:
|
||||||
1. Configure 'config.json' using 'config.example.json' as a template.
|
1. Configure 'config.json' using 'config.example.json' as a template.
|
||||||
2. Enter MQTT credentials and VAPID keys.
|
2. Enter MQTT credentials and VAPID keys.
|
||||||
@@ -32,6 +29,10 @@ Steps:
|
|||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
Required to enable browser and mobile notifications.
|
Required to enable browser and mobile notifications.
|
||||||
|
|
||||||
|
⚠️ WARNING: Web Push Notifications strictly require the
|
||||||
|
dashboard to be accessed via a secure HTTPS connection
|
||||||
|
(or localhost). They will NOT work over standard HTTP.
|
||||||
|
|
||||||
1. Go to https://vapidkeys.com/ and generate the keys.
|
1. Go to https://vapidkeys.com/ and generate the keys.
|
||||||
2. Copy 'Public Key' and 'Private Key' into 'config.json'.
|
2. Copy 'Public Key' and 'Private Key' into 'config.json'.
|
||||||
3. Set 'vapid_claim_email' (e.g., "mailto:your@email.com").
|
3. Set 'vapid_claim_email' (e.g., "mailto:your@email.com").
|
||||||
@@ -40,7 +41,6 @@ Required to enable browser and mobile notifications.
|
|||||||
4. AGENT SETUP (REMOTE NODES)
|
4. AGENT SETUP (REMOTE NODES)
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
To be installed on each Raspberry Pi / MMDVM Node.
|
To be installed on each Raspberry Pi / MMDVM Node.
|
||||||
|
|
||||||
1. Copy 'system_monitor.py' and 'node_config.json' to
|
1. Copy 'system_monitor.py' and 'node_config.json' to
|
||||||
'/opt/node_agent/'.
|
'/opt/node_agent/'.
|
||||||
2. Edit 'node_config.json' with a unique 'client_id'.
|
2. Edit 'node_config.json' with a unique 'client_id'.
|
||||||
@@ -50,7 +50,6 @@ To be installed on each Raspberry Pi / MMDVM Node.
|
|||||||
5. RUNNING AS A SERVICE (SYSTEMD)
|
5. RUNNING AS A SERVICE (SYSTEMD)
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
For auto-start and process monitoring.
|
For auto-start and process monitoring.
|
||||||
|
|
||||||
Configuration:
|
Configuration:
|
||||||
1. Copy .service files to '/etc/systemd/system/':
|
1. Copy .service files to '/etc/systemd/system/':
|
||||||
- Server: sudo cp fleet-console.service /etc/systemd/system/
|
- Server: sudo cp fleet-console.service /etc/systemd/system/
|
||||||
@@ -69,14 +68,12 @@ Configuration:
|
|||||||
|
|
||||||
Questa guida descrive i passaggi per installare la Dashboard
|
Questa guida descrive i passaggi per installare la Dashboard
|
||||||
Centrale e gli Agenti Remoti sui nodi MMDVM.
|
Centrale e gli Agenti Remoti sui nodi MMDVM.
|
||||||
|
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
1. REQUISITI PRELIMINARI
|
1. REQUISITI PRELIMINARI
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
Assicurarsi di avere Python 3 installato su tutti i sistemi.
|
Assicurarsi di avere Python 3 installato su tutti i sistemi.
|
||||||
Le dipendenze necessarie sono elencate nel file
|
Le dipendenze necessarie sono elencate nel file
|
||||||
'requirements.txt'.
|
'requirements.txt'.
|
||||||
|
|
||||||
Installazione dipendenze:
|
Installazione dipendenze:
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
|
||||||
@@ -84,7 +81,6 @@ Installazione dipendenze:
|
|||||||
2. SETUP DEL SERVER (HUB CENTRALE)
|
2. SETUP DEL SERVER (HUB CENTRALE)
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
Il server gestisce l'interfaccia web e i permessi.
|
Il server gestisce l'interfaccia web e i permessi.
|
||||||
|
|
||||||
Passaggi:
|
Passaggi:
|
||||||
1. Configura 'config.json' partendo da 'config.example.json'.
|
1. Configura 'config.json' partendo da 'config.example.json'.
|
||||||
2. Inserisci le credenziali MQTT e le chiavi VAPID.
|
2. Inserisci le credenziali MQTT e le chiavi VAPID.
|
||||||
@@ -97,6 +93,10 @@ Passaggi:
|
|||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
Necessarie per abilitare le notifiche su browser e mobile.
|
Necessarie per abilitare le notifiche su browser e mobile.
|
||||||
|
|
||||||
|
⚠️ ATTENZIONE: Le notifiche push richiedono tassativamente
|
||||||
|
che la dashboard sia accessibile tramite una connessione
|
||||||
|
sicura HTTPS. I browser bloccano la funzione su HTTP normale.
|
||||||
|
|
||||||
1. Vai su https://vapidkeys.com/ e genera le chiavi.
|
1. Vai su https://vapidkeys.com/ e genera le chiavi.
|
||||||
2. Copia 'Public Key' e 'Private Key' nel 'config.json'.
|
2. Copia 'Public Key' e 'Private Key' nel 'config.json'.
|
||||||
3. Imposta 'vapid_claim_email' (es. "mailto:tua@email.com").
|
3. Imposta 'vapid_claim_email' (es. "mailto:tua@email.com").
|
||||||
@@ -105,7 +105,6 @@ Necessarie per abilitare le notifiche su browser e mobile.
|
|||||||
4. SETUP DELL'AGENTE (NODI REMOTI)
|
4. SETUP DELL'AGENTE (NODI REMOTI)
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
Da installare su ogni Raspberry Pi / Nodo MMDVM.
|
Da installare su ogni Raspberry Pi / Nodo MMDVM.
|
||||||
|
|
||||||
1. Copia 'system_monitor.py' e 'node_config.json' in
|
1. Copia 'system_monitor.py' e 'node_config.json' in
|
||||||
'/opt/node_agent/'.
|
'/opt/node_agent/'.
|
||||||
2. Modifica 'node_config.json' con il 'client_id' univoco.
|
2. Modifica 'node_config.json' con il 'client_id' univoco.
|
||||||
@@ -115,7 +114,6 @@ Da installare su ogni Raspberry Pi / Nodo MMDVM.
|
|||||||
5. ESECUZIONE COME SERVIZIO (SYSTEMD)
|
5. ESECUZIONE COME SERVIZIO (SYSTEMD)
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
Per l'avvio automatico e il monitoraggio del processo.
|
Per l'avvio automatico e il monitoraggio del processo.
|
||||||
|
|
||||||
Configurazione:
|
Configurazione:
|
||||||
1. Copia i file .service in '/etc/systemd/system/':
|
1. Copia i file .service in '/etc/systemd/system/':
|
||||||
- Server: sudo cp fleet-console.service /etc/systemd/system/
|
- Server: sudo cp fleet-console.service /etc/systemd/system/
|
||||||
|
|||||||
+13
-2
@@ -795,7 +795,19 @@
|
|||||||
const res = await fetch('/api/logs'); const logs = await res.json();
|
const res = await fetch('/api/logs'); const logs = await res.json();
|
||||||
const tbody = document.getElementById('log-body'); let tableHTML = ""; let networkLogs = {};
|
const tbody = document.getElementById('log-body'); let tableHTML = ""; let networkLogs = {};
|
||||||
logs.forEach(row => {
|
logs.forEach(row => {
|
||||||
const time = row[0] ? row[0].split(' ')[1] : "--:--"; const clientId = row[1]; const protocol = row[2] || "DMR"; const source = row[3] || "---"; const target = row[4] || "---"; const rawSlot = row[5];
|
const time = row[0] ? row[0].split(' ')[1] : "--:--";
|
||||||
|
const clientId = row[1];
|
||||||
|
const protocol = row[2] || "DMR";
|
||||||
|
let source = row[3] || "---";
|
||||||
|
const target = row[4] || "---";
|
||||||
|
const rawSlot = row[5];
|
||||||
|
const source_ext = row[8]; // <--- NUOVO CAMPO DAL BACKEND
|
||||||
|
|
||||||
|
// Formattiamo il source_ext se presente e non vuoto
|
||||||
|
if (source_ext && source_ext.trim() !== "") {
|
||||||
|
source = `${source} <span style="font-size:0.8em; color:#94a3b8; font-family:'JetBrains Mono', monospace;">/${source_ext}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
const slotDisplay = protocol === "DMR" ? `TS${rawSlot}` : "--";
|
const slotDisplay = protocol === "DMR" ? `TS${rawSlot}` : "--";
|
||||||
let protoColor = "#3b82f6"; if (protocol === "NXDN") protoColor = "#10b981"; else if (protocol === "YSF") protoColor = "#8b5cf6"; else if (protocol === "D-STAR") protoColor = "#06b6d4"; else if (protocol === "P25") protoColor = "#f59e0b";
|
let protoColor = "#3b82f6"; if (protocol === "NXDN") protoColor = "#10b981"; else if (protocol === "YSF") protoColor = "#8b5cf6"; else if (protocol === "D-STAR") protoColor = "#06b6d4"; else if (protocol === "P25") protoColor = "#f59e0b";
|
||||||
|
|
||||||
@@ -810,7 +822,6 @@
|
|||||||
clients.forEach(c => { const localDiv = document.getElementById(`sys-log-${c.id}`); if (localDiv && networkLogs[c.id]) { localDiv.innerHTML = networkLogs[c.id]; } });
|
clients.forEach(c => { const localDiv = document.getElementById(`sys-log-${c.id}`); if (localDiv && networkLogs[c.id]) { localDiv.innerHTML = networkLogs[c.id]; } });
|
||||||
} catch (e) { console.error(e); }
|
} catch (e) { console.error(e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- USER MANAGEMENT (ADD & EDIT) ---
|
// --- USER MANAGEMENT (ADD & EDIT) ---
|
||||||
async function openAdmin() { document.getElementById('admin-modal').style.display = 'flex'; loadUsers(); loadSettings(); cancelEdit(); }
|
async function openAdmin() { document.getElementById('admin-modal').style.display = 'flex'; loadUsers(); loadSettings(); cancelEdit(); }
|
||||||
function closeAdmin() { document.getElementById('admin-modal').style.display = 'none'; cancelEdit(); }
|
function closeAdmin() { document.getElementById('admin-modal').style.display = 'none'; cancelEdit(); }
|
||||||
|
|||||||
Reference in New Issue
Block a user