Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb9fcea704 | ||
|
|
171b9d587a | ||
|
|
97214efe89 | ||
|
|
559d6cd177 | ||
|
|
8a4a8797c4 | ||
|
|
b116b6d0be | ||
|
|
f034b435de | ||
|
|
279ca84aa1 | ||
|
|
78a406a4aa | ||
|
|
0a9d2d9466 |
@@ -7,7 +7,7 @@
|
|||||||
<a name="english"></a>
|
<a name="english"></a>
|
||||||
## 🇬🇧 English
|
## 🇬🇧 English
|
||||||
|
|
||||||
The **Fleet Control Agent** is a lightweight monitoring and control script designed for remote MMDVM nodes. It acts as the bridge between your radio hardware and the Central Console.
|
The **Fleet Node Agent** is a lightweight monitoring and control script designed for remote MMDVM nodes. It acts as the bridge between your radio hardware and the Central Console.
|
||||||
|
|
||||||
> ℹ️ **Note:** This is the Remote Agent repository, intended to be installed on individual repeater nodes. To manage and monitor all your fleet from a single web dashboard, you must also install the [Fleet Control Console (Server)](https://git.arifvg.it/iv3jdv/fleet-control-server) on your central hub.
|
> ℹ️ **Note:** This is the Remote Agent repository, intended to be installed on individual repeater nodes. To manage and monitor all your fleet from a single web dashboard, you must also install the [Fleet Control Console (Server)](https://git.arifvg.it/iv3jdv/fleet-control-server) on your central hub.
|
||||||
|
|
||||||
@@ -18,9 +18,21 @@ The **Fleet Control Agent** is a lightweight monitoring and control script desig
|
|||||||
* **Hardware Reset:** Supports physical MMDVM HAT reset via GPIO pins (requires RPi.GPIO).
|
* **Hardware Reset:** Supports physical MMDVM HAT reset via GPIO pins (requires RPi.GPIO).
|
||||||
* **Auto-Healing:** Automatically detects service failures and attempts to restart them.
|
* **Auto-Healing:** Automatically detects service failures and attempts to restart them.
|
||||||
|
|
||||||
|
### 🔄 Dynamic Profiles (Multiple Configurations)
|
||||||
|
The agent supports unlimited dynamic profile switching (e.g., switching between a BrandMeister and a DMR+ configuration on the fly).
|
||||||
|
|
||||||
|
In your `node_config.json`, you can define any number of custom profiles under the `"profiles"` key. For each profile, specify a label and the list of services/files to swap.
|
||||||
|
The Central Console dashboard will automatically read this configuration and dynamically generate a button for each profile. If you don't need profile switching for a specific node, simply leave the dictionary empty (`"profiles": {}` or remove the block entirely). The dashboard will gracefully hide the profile controls for that node, keeping the UI clean.
|
||||||
|
|
||||||
### 🤖 Telegram Bot Integration
|
### 🤖 Telegram Bot Integration
|
||||||
The agent features built-in support for **Telegram Bot** notifications. This allows system administrators to receive real-time alerts regarding node status, offline services, and critical system events directly on their mobile devices. Notifications are highly manageable: they can be dynamically enabled or muted for each individual node directly from the central NOC Fleet Console, ensuring you only get notified when it matters.
|
The agent features built-in support for **Telegram Bot** notifications. This allows system administrators to receive real-time alerts regarding node status, offline services, and critical system events directly on their mobile devices. Notifications are highly manageable: they can be dynamically enabled or muted for each individual node directly from the central NOC Fleet Console, ensuring you only get notified when it matters.
|
||||||
|
|
||||||
|
### 🔌 Dynamic Hardware Resets (USB & GPIO)
|
||||||
|
The agent can perform surgical hardware resets to stuck modems without rebooting the entire node. You can configure this dynamically in your `node_config.json` under the `"settings"` block:
|
||||||
|
* **USB Modems (e.g., STM32 Nucleo):** Set `"usb_reset_id"` to your device's ID (e.g., `"0483:374b"`). This requires the `usbutils` package installed on the host.
|
||||||
|
* **GPIO HATs:** Set `"gpio_reset_pin"` to your specific BCM pin (e.g., `21`).
|
||||||
|
The agent will automatically prioritize the USB reset. If `"usb_reset_id"` is left empty (`""`), it will gracefully fall back to the GPIO method.
|
||||||
|
|
||||||
### 🛠️ Core Configuration Files
|
### 🛠️ Core Configuration Files
|
||||||
To enable full functionality, you must configure these three files:
|
To enable full functionality, you must configure these three files:
|
||||||
* **`node_config.json`**: The main configuration. Here you set your MQTT broker, your unique `client_id` (e.g., `IR3XXX`), and the paths for the lists below.
|
* **`node_config.json`**: The main configuration. Here you set your MQTT broker, your unique `client_id` (e.g., `IR3XXX`), and the paths for the lists below.
|
||||||
@@ -58,7 +70,7 @@ pip install -r requirements.txt
|
|||||||
|
|
||||||
#### 4. Systemd Service (Auto-start)
|
#### 4. Systemd Service (Auto-start)
|
||||||
```bash
|
```bash
|
||||||
cp fleet-agent.service /etc/systemd/system/
|
cp systemd/fleet-agent.service /etc/systemd/system/
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable fleet-agent
|
systemctl enable fleet-agent
|
||||||
systemctl start fleet-agent
|
systemctl start fleet-agent
|
||||||
@@ -69,7 +81,7 @@ systemctl start fleet-agent
|
|||||||
<a name="italiano"></a>
|
<a name="italiano"></a>
|
||||||
## 🇮🇹 Italiano
|
## 🇮🇹 Italiano
|
||||||
|
|
||||||
Il **Fleet Control Agent** è lo script di monitoraggio e controllo per i nodi remoti MMDVM. Funge da ponte tra l'hardware radio e la Console Centrale.
|
Il **Fleet Node Agent** è lo script di monitoraggio e controllo per i nodi remoti MMDVM. Funge da ponte tra l'hardware radio e la Console Centrale.
|
||||||
|
|
||||||
> ℹ️ **Nota:** Questo è il repository dell'Agent Remoto, da installare sui singoli nodi ripetitore. Per gestire e monitorare tutta la flotta da un'unica dashboard web, devi installare anche la [Fleet Control Console (Server)](https://git.arifvg.it/iv3jdv/fleet-control-server) sul tuo hub centrale.
|
> ℹ️ **Nota:** Questo è il repository dell'Agent Remoto, da installare sui singoli nodi ripetitore. Per gestire e monitorare tutta la flotta da un'unica dashboard web, devi installare anche la [Fleet Control Console (Server)](https://git.arifvg.it/iv3jdv/fleet-control-server) sul tuo hub centrale.
|
||||||
|
|
||||||
@@ -80,9 +92,21 @@ Il **Fleet Control Agent** è lo script di monitoraggio e controllo per i nodi r
|
|||||||
* **Reset Hardware:** Supporta il reset fisico della scheda MMDVM HAT tramite pin GPIO (richiede RPi.GPIO).
|
* **Reset Hardware:** Supporta il reset fisico della scheda MMDVM HAT tramite pin GPIO (richiede RPi.GPIO).
|
||||||
* **Auto-Healing:** Rileva automaticamente i crash dei servizi e tenta di riavviarli.
|
* **Auto-Healing:** Rileva automaticamente i crash dei servizi e tenta di riavviarli.
|
||||||
|
|
||||||
|
### 🔄 Profili Dinamici Multipli (Dynamic Profiles)
|
||||||
|
L'agent supporta il cambio rapido tra un numero illimitato di configurazioni (es. per passare "al volo" da un setup BrandMeister a uno DMR+).
|
||||||
|
|
||||||
|
Nel file `node_config.json`, puoi definire quanti profili desideri sotto la chiave `"profiles"`. Per ogni profilo, ti basta specificare un'etichetta (label) e i servizi/file da scambiare.
|
||||||
|
La dashboard centrale leggerà questa configurazione e genererà dinamicamente un pulsante per ogni profilo inserito. Se un nodo non necessita di questa funzione, è sufficiente lasciare il dizionario vuoto (`"profiles": {}` o rimuovere del tutto il blocco). La dashboard capirà in automatico di dover nascondere i controlli dei profili per quel nodo specifico, mantenendo l'interfaccia pulita.
|
||||||
|
|
||||||
### 🤖 Telegram Bot Integration
|
### 🤖 Telegram Bot Integration
|
||||||
L'agent dispone del supporto nativo per le notifiche tramite **Bot Telegram**. Questo permette agli amministratori di sistema di ricevere alert in tempo reale sullo stato dei nodi, sui servizi offline e sugli eventi critici direttamente sul proprio smartphone. La gestione degli avvisi è centralizzata: le notifiche possono essere attivate o silenziate dinamicamente per ogni singolo nodo direttamente dalla Fleet Console centrale, evitando spam inutile.
|
L'agent dispone del supporto nativo per le notifiche tramite **Bot Telegram**. Questo permette agli amministratori di sistema di ricevere alert in tempo reale sullo stato dei nodi, sui servizi offline e sugli eventi critici direttamente sul proprio smartphone. La gestione degli avvisi è centralizzata: le notifiche possono essere attivate o silenziate dinamicamente per ogni singolo nodo direttamente dalla Fleet Console centrale, evitando spam inutile.
|
||||||
|
|
||||||
|
### 🔌 Reset Hardware Dinamico (USB & GPIO)
|
||||||
|
L'agente può eseguire reset hardware "chirurgici" ai modem bloccati senza dover riavviare l'intero nodo. Puoi configurare questa funzione dinamicamente nel file `node_config.json` sotto il blocco `"settings"`:
|
||||||
|
* **Modem USB (es. STM32 Nucleo):** Imposta `"usb_reset_id"` con l'ID del tuo dispositivo (es. `"0483:374b"`). Richiede il pacchetto `usbutils` installato sul nodo.
|
||||||
|
* **HAT GPIO:** Imposta `"gpio_reset_pin"` con il tuo pin BCM specifico (es. `21`).
|
||||||
|
L'agente darà sempre priorità al reset USB. Se il campo `"usb_reset_id"` viene lasciato vuoto (`""`), il sistema ripiegherà automaticamente sul metodo GPIO.
|
||||||
|
|
||||||
### 🛠️ File di Configurazione Chiave
|
### 🛠️ File di Configurazione Chiave
|
||||||
Per il corretto funzionamento, è necessario definire i parametri in questi tre file:
|
Per il corretto funzionamento, è necessario definire i parametri in questi tre file:
|
||||||
* **`node_config.json`**: La configurazione principale. Qui imposti il broker MQTT, il tuo `client_id` univoco (es. `IR3XXX`) e i percorsi per le liste sottostanti.
|
* **`node_config.json`**: La configurazione principale. Qui imposti il broker MQTT, il tuo `client_id` univoco (es. `IR3XXX`) e i percorsi per le liste sottostanti.
|
||||||
@@ -120,7 +144,7 @@ pip install -r requirements.txt
|
|||||||
|
|
||||||
#### 4. Esecuzione come Servizio (systemd)
|
#### 4. Esecuzione come Servizio (systemd)
|
||||||
```bash
|
```bash
|
||||||
cp fleet-agent.service /etc/systemd/system/
|
cp systemd/fleet-agent.service /etc/systemd/system/
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable fleet-agent
|
systemctl enable fleet-agent
|
||||||
systemctl start fleet-agent
|
systemctl start fleet-agent
|
||||||
|
|||||||
+30
-2
@@ -46,10 +46,24 @@ C) REMOTE EDITING FILES (file_list.txt):
|
|||||||
D) OPTIONAL:
|
D) OPTIONAL:
|
||||||
Install 'RPi.GPIO' if you want hardware reset. [cite: 14]
|
Install 'RPi.GPIO' if you want hardware reset. [cite: 14]
|
||||||
|
|
||||||
|
E) MULTIPLE PROFILES (Dynamic Profiles):
|
||||||
|
In 'node_config.json', you can define custom setup profiles
|
||||||
|
under the "profiles" block. The web dashboard will automatically
|
||||||
|
generate a dedicated button for each profile you define here.
|
||||||
|
If you don't need this feature, simply leave it empty:
|
||||||
|
"profiles": {}
|
||||||
|
|
||||||
|
F) HARDWARE RESET CONFIGURATION:
|
||||||
|
In 'node_config.json' under "settings", define your hardware
|
||||||
|
reset method to recover stuck modems automatically:
|
||||||
|
- For USB modems: set "usb_reset_id" (e.g., "0483:374b").
|
||||||
|
- For GPIO HATs: set "gpio_reset_pin" (e.g., 21).
|
||||||
|
Leave "usb_reset_id" empty ("") to force GPIO usage.
|
||||||
|
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
3. RUNNING AS A SERVICE (SYSTEMD)
|
3. RUNNING AS A SERVICE (SYSTEMD)
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
1. cp fleet-agent.service /etc/systemd/system/
|
1. cp systemd/fleet-agent.service /etc/systemd/system/
|
||||||
2. systemctl daemon-reload
|
2. systemctl daemon-reload
|
||||||
3. systemctl enable fleet-agent
|
3. systemctl enable fleet-agent
|
||||||
4. systemctl start fleet-agent
|
4. systemctl start fleet-agent
|
||||||
@@ -103,10 +117,24 @@ C) EDITING FILE REMOTO (file_list.txt):
|
|||||||
D) OPZIONALE:
|
D) OPZIONALE:
|
||||||
Installa 'RPi.GPIO' per il reset hardware. [cite: 17]
|
Installa 'RPi.GPIO' per il reset hardware. [cite: 17]
|
||||||
|
|
||||||
|
E) PROFILI MULTIPLI (Dynamic Profiles):
|
||||||
|
In 'node_config.json', puoi definire profili personalizzati
|
||||||
|
sotto il blocco "profiles". La dashboard web genererà in
|
||||||
|
automatico un tasto dedicato per ogni profilo inserito.
|
||||||
|
Se non ti serve questa funzione, lascialo vuoto:
|
||||||
|
"profiles": {}
|
||||||
|
|
||||||
|
F) CONFIGURAZIONE RESET HARDWARE:
|
||||||
|
In 'node_config.json' sotto "settings", definisci il metodo
|
||||||
|
di reset per sbloccare i modem bloccati:
|
||||||
|
- Per modem USB: imposta "usb_reset_id" (es. "0483:374b").
|
||||||
|
- Per HAT GPIO: imposta "gpio_reset_pin" (es. 21).
|
||||||
|
Lascia "usb_reset_id" vuoto ("") per forzare l'uso del GPIO.
|
||||||
|
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
3. ESECUZIONE COME SERVIZIO (SYSTEMD)
|
3. ESECUZIONE COME SERVIZIO (SYSTEMD)
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
1. cp fleet-agent.service /etc/systemd/system/
|
1. cp systemd/fleet-agent.service /etc/systemd/system/
|
||||||
2. systemctl daemon-reload
|
2. systemctl daemon-reload
|
||||||
3. systemctl enable fleet-agent
|
3. systemctl enable fleet-agent
|
||||||
4. systemctl start fleet-agent
|
4. systemctl start fleet-agent
|
||||||
|
|||||||
@@ -13,7 +13,9 @@
|
|||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"auto_healing": true,
|
"auto_healing": true,
|
||||||
"update_interval": 60
|
"update_interval": 60,
|
||||||
|
"usb_reset_id": "",
|
||||||
|
"gpio_reset_pin": 21
|
||||||
},
|
},
|
||||||
"telegram": {
|
"telegram": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
|
|||||||
+100
-19
@@ -199,25 +199,36 @@ def check_auto_healing(client, status):
|
|||||||
client.publish(f"devices/{CLIENT_ID}/logs", msg)
|
client.publish(f"devices/{CLIENT_ID}/logs", msg)
|
||||||
send_telegram_message(msg)
|
send_telegram_message(msg)
|
||||||
|
|
||||||
# --- START MODIFICATION: SPECIFIC HARDWARE RESET FOR MMDVMHOST ---
|
# --- INIZIO LOGICA RESET HARDWARE PER MMDVMHOST ---
|
||||||
if proc_name.lower() == "mmdvmhost" and GPIO_AVAILABLE:
|
if proc_name.lower() == "mmdvmhost":
|
||||||
logger.info("Executing automatic HAT RESET before restarting MMDVMHost...")
|
usb_id = cfg.get('settings', {}).get('usb_reset_id', "").strip()
|
||||||
|
gpio_pin = cfg.get('settings', {}).get('gpio_reset_pin', 21)
|
||||||
|
|
||||||
|
if usb_id:
|
||||||
|
logger.info(f"Esecuzione reset USB per il dispositivo {usb_id} prima del riavvio...")
|
||||||
try:
|
try:
|
||||||
RESET_PIN = 21 # Ensure the PIN is correct for your nodes
|
subprocess.run(["sudo", "usbreset", usb_id], check=False)
|
||||||
|
time.sleep(1.5)
|
||||||
|
client.publish(f"devices/{CLIENT_ID}/logs", f"🔌 USB Reset ({usb_id}) inviato!")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Errore durante il reset USB in auto-healing: {e}")
|
||||||
|
|
||||||
|
elif GPIO_AVAILABLE and gpio_pin:
|
||||||
|
logger.info(f"Esecuzione reset GPIO (HAT) sul pin {gpio_pin} prima del riavvio...")
|
||||||
|
try:
|
||||||
|
RESET_PIN = int(gpio_pin)
|
||||||
GPIO.setwarnings(False)
|
GPIO.setwarnings(False)
|
||||||
GPIO.setmode(GPIO.BCM)
|
GPIO.setmode(GPIO.BCM)
|
||||||
GPIO.setup(RESET_PIN, GPIO.OUT)
|
GPIO.setup(RESET_PIN, GPIO.OUT)
|
||||||
# LOW pulse to reset
|
|
||||||
GPIO.output(RESET_PIN, GPIO.LOW)
|
GPIO.output(RESET_PIN, GPIO.LOW)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
GPIO.output(RESET_PIN, GPIO.HIGH)
|
GPIO.output(RESET_PIN, GPIO.HIGH)
|
||||||
GPIO.cleanup(RESET_PIN)
|
GPIO.cleanup(RESET_PIN)
|
||||||
# Give the microcontroller time to restart
|
|
||||||
time.sleep(1.5)
|
time.sleep(1.5)
|
||||||
client.publish(f"devices/{CLIENT_ID}/logs", "🔌 GPIO Pulse (MMDVM Reset) sent!")
|
client.publish(f"devices/{CLIENT_ID}/logs", f"🔌 GPIO Pulse (Pin {RESET_PIN}) inviato!")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"GPIO error in auto-healing: {e}")
|
logger.error(f"Errore GPIO in auto-healing: {e}")
|
||||||
# --- END MODIFICATION ---
|
# --- FINE LOGICA MODIFICA ---
|
||||||
|
|
||||||
subprocess.run(["sudo", "systemctl", "restart", proc_name])
|
subprocess.run(["sudo", "systemctl", "restart", proc_name])
|
||||||
elif attempts == 3:
|
elif attempts == 3:
|
||||||
@@ -329,8 +340,8 @@ def on_connect(client, userdata, flags, reason_code, properties=None):
|
|||||||
logger.error(f"❌ MQTT connection error. Code: {reason_code}")
|
logger.error(f"❌ MQTT connection error. Code: {reason_code}")
|
||||||
|
|
||||||
def on_disconnect(client, userdata, disconnect_flags, reason_code, properties=None):
|
def on_disconnect(client, userdata, disconnect_flags, reason_code, properties=None):
|
||||||
logger.warning(f"⚠️ Disconnected from MQTT broker! Code: {reason_code}")
|
logger.error(f"⚠️ Disconnessione rilevata a basso livello (Codice: {reason_code}). Forzo il riavvio tramite systemd...")
|
||||||
logger.info("Waiting for network return. Paho-MQTT will attempt automatic reconnection...")
|
os._exit(1)
|
||||||
|
|
||||||
def on_message(client, userdata, msg):
|
def on_message(client, userdata, msg):
|
||||||
global boot_recovered, current_status, cfg
|
global boot_recovered, current_status, cfg
|
||||||
@@ -350,15 +361,54 @@ def on_message(client, userdata, msg):
|
|||||||
client.publish(TOPIC_STAT, current_status, retain=True)
|
client.publish(TOPIC_STAT, current_status, retain=True)
|
||||||
boot_recovered = True
|
boot_recovered = True
|
||||||
publish_all(client)
|
publish_all(client)
|
||||||
|
|
||||||
|
elif cmd == "UPDATE_AGENT":
|
||||||
|
client.publish(f"devices/{CLIENT_ID}/logs", "🔄 Avvio aggiornamento OTA (git pull)...")
|
||||||
|
logger.info("Ricevuto comando di UPDATE_AGENT. Esecuzione git pull...")
|
||||||
|
try:
|
||||||
|
# Esegue il comando git pull nella directory dell'agente
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "pull"],
|
||||||
|
cwd=AGENT_DIR,
|
||||||
|
capture_output=True, text=True, check=True
|
||||||
|
)
|
||||||
|
client.publish(f"devices/{CLIENT_ID}/logs", f"✅ Git pull completato. Riavvio in corso...")
|
||||||
|
logger.info(f"Aggiornamento completato:\n{result.stdout}")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# Uccide il processo. Systemd lo farà ripartire istantaneamente con il nuovo codice!
|
||||||
|
os._exit(1)
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
client.publish(f"devices/{CLIENT_ID}/logs", f"❌ Errore aggiornamento:\n{e.stderr}")
|
||||||
|
logger.error(f"Errore git pull:\n{e.stderr}")
|
||||||
|
|
||||||
elif cmd == "REBOOT":
|
elif cmd == "REBOOT":
|
||||||
client.publish(TOPIC_STAT, f"OFFLINE - Rebooting {CLIENT_ID.upper()}...", retain=False)
|
client.publish(TOPIC_STAT, f"OFFLINE - Rebooting {CLIENT_ID.upper()}...", retain=False)
|
||||||
logger.info("REBOOT command received. Rebooting system...")
|
logger.info("REBOOT command received. Rebooting system...")
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
subprocess.run(["sudo", "reboot"], check=True)
|
subprocess.run(["sudo", "reboot"], check=True)
|
||||||
|
|
||||||
elif cmd == 'RESET_HAT':
|
elif cmd == 'RESET_HAT':
|
||||||
RESET_PIN = 21
|
usb_id = cfg.get('settings', {}).get('usb_reset_id', "").strip()
|
||||||
if GPIO_AVAILABLE:
|
gpio_pin = cfg.get('settings', {}).get('gpio_reset_pin', 21)
|
||||||
|
|
||||||
|
if usb_id:
|
||||||
try:
|
try:
|
||||||
|
logger.info(f"RESET USB manuale inviato al dispositivo {usb_id}")
|
||||||
|
subprocess.run(["sudo", "usbreset", usb_id], check=False)
|
||||||
|
time.sleep(1.5)
|
||||||
|
logger.info("Restarting MMDVMHost...")
|
||||||
|
subprocess.run(["sudo", "systemctl", "restart", "mmdvmhost"], check=False)
|
||||||
|
client.publish(f"fleet/{CLIENT_ID}/status", "USB RESET + MMDVM RESTART OK")
|
||||||
|
client.publish(f"devices/{CLIENT_ID}/logs", f"🔌 USB Reset ({usb_id}) + MMDVMHost Restarted")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Errore durante il reset USB/MMDVMHost: {e}")
|
||||||
|
client.publish(f"fleet/{CLIENT_ID}/status", f"RESET ERROR: {e}")
|
||||||
|
|
||||||
|
elif GPIO_AVAILABLE and gpio_pin:
|
||||||
|
try:
|
||||||
|
RESET_PIN = int(gpio_pin)
|
||||||
GPIO.setwarnings(False)
|
GPIO.setwarnings(False)
|
||||||
GPIO.setmode(GPIO.BCM)
|
GPIO.setmode(GPIO.BCM)
|
||||||
GPIO.setup(RESET_PIN, GPIO.OUT)
|
GPIO.setup(RESET_PIN, GPIO.OUT)
|
||||||
@@ -366,15 +416,19 @@ def on_message(client, userdata, msg):
|
|||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
GPIO.output(RESET_PIN, GPIO.HIGH)
|
GPIO.output(RESET_PIN, GPIO.HIGH)
|
||||||
GPIO.cleanup(RESET_PIN)
|
GPIO.cleanup(RESET_PIN)
|
||||||
logger.info(f"RESET pulse sent to GPIO {RESET_PIN}")
|
logger.info(f"Pulsazione di RESET inviata al GPIO {RESET_PIN}")
|
||||||
time.sleep(1.5)
|
time.sleep(1.5)
|
||||||
logger.info("Restarting MMDVMHost...")
|
logger.info("Restarting MMDVMHost...")
|
||||||
subprocess.run(["sudo", "systemctl", "restart", "mmdvmhost"], check=False)
|
subprocess.run(["sudo", "systemctl", "restart", "mmdvmhost"], check=False)
|
||||||
client.publish(f"fleet/{CLIENT_ID}/status", "HAT RESET + MMDVM RESTART OK")
|
client.publish(f"fleet/{CLIENT_ID}/status", "HAT RESET + MMDVM RESTART OK")
|
||||||
client.publish(f"devices/{CLIENT_ID}/logs", "🔌 HAT Reset + MMDVMHost Restarted")
|
client.publish(f"devices/{CLIENT_ID}/logs", f"🔌 GPIO Reset (Pin {RESET_PIN}) + MMDVMHost Restarted")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error during GPIO/MMDVMHost reset: {e}")
|
logger.error(f"Errore durante il reset GPIO/MMDVMHost: {e}")
|
||||||
client.publish(f"fleet/{CLIENT_ID}/status", f"RESET ERROR: {e}")
|
client.publish(f"fleet/{CLIENT_ID}/status", f"RESET ERROR: {e}")
|
||||||
|
else:
|
||||||
|
msg = "⚠️ Nessun metodo di reset (USB o GPIO) configurato o disponibile sul nodo."
|
||||||
|
logger.warning(msg)
|
||||||
|
client.publish(f"devices/{CLIENT_ID}/logs", msg)
|
||||||
|
|
||||||
elif cmd in ["TG:OFF", "TG:ON"]:
|
elif cmd in ["TG:OFF", "TG:ON"]:
|
||||||
new_state = (cmd == "TG:ON")
|
new_state = (cmd == "TG:ON")
|
||||||
@@ -389,7 +443,6 @@ def on_message(client, userdata, msg):
|
|||||||
logger.info("📥 Received global update command (REQ CONFIG)")
|
logger.info("📥 Received global update command (REQ CONFIG)")
|
||||||
publish_all(client)
|
publish_all(client)
|
||||||
publish_all_ini_files(client)
|
publish_all_ini_files(client)
|
||||||
# Force the visual update of the card on the dashboard!
|
|
||||||
client.publish(TOPIC_STAT, current_status, retain=True)
|
client.publish(TOPIC_STAT, current_status, retain=True)
|
||||||
|
|
||||||
elif topic == f"devices/{CLIENT_ID}/control":
|
elif topic == f"devices/{CLIENT_ID}/control":
|
||||||
@@ -413,11 +466,39 @@ def on_message(client, userdata, msg):
|
|||||||
publish_all_ini_files(client)
|
publish_all_ini_files(client)
|
||||||
|
|
||||||
def auto_publish_task(client):
|
def auto_publish_task(client):
|
||||||
|
disconnect_counter = 0
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
try:
|
||||||
|
# Forza la conversione a intero per evitare crash silenziosi su time.sleep
|
||||||
|
update_interval = int(cfg['settings'].get('update_interval', 30))
|
||||||
|
|
||||||
|
if not client.is_connected():
|
||||||
|
disconnect_counter += 1
|
||||||
|
logger.warning(f"⚠️ Broker MQTT irraggiungibile. Watchdog {disconnect_counter}/5...")
|
||||||
|
|
||||||
|
if disconnect_counter >= 5:
|
||||||
|
logger.error("🚨 Connessione persa definitivamente. Hard Kill del processo...")
|
||||||
|
os._exit(1)
|
||||||
|
else:
|
||||||
|
if disconnect_counter > 0:
|
||||||
|
logger.info("✅ Connessione MQTT ripristinata spontaneamente.")
|
||||||
|
disconnect_counter = 0
|
||||||
|
|
||||||
|
# Le funzioni di sistema e file IO sono ora blindate per non far crashare il loop
|
||||||
status = publish_all(client)
|
status = publish_all(client)
|
||||||
publish_all_ini_files(client)
|
publish_all_ini_files(client)
|
||||||
check_auto_healing(client, status)
|
check_auto_healing(client, status)
|
||||||
time.sleep(cfg['settings'].get('update_interval', 30))
|
|
||||||
|
except Exception as e:
|
||||||
|
# Cattura e scrive nei log qualsiasi anomalia interna che prima uccideva il thread
|
||||||
|
logger.error(f"❌ Errore critico nel thread Watchdog: {e}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
time.sleep(update_interval)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"⚠️ Errore nel timer di sleep: {e}")
|
||||||
|
time.sleep(30) # Fallback
|
||||||
|
|
||||||
def start_service():
|
def start_service():
|
||||||
global current_status
|
global current_status
|
||||||
@@ -435,7 +516,7 @@ def start_service():
|
|||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
logger.info("Attempting connection to MQTT broker...")
|
logger.info("Attempting connection to MQTT broker...")
|
||||||
client.connect(cfg['mqtt']['broker'], cfg['mqtt']['port'], 60)
|
client.connect(cfg['mqtt']['broker'], cfg['mqtt']['port'], 15)
|
||||||
|
|
||||||
# 2. Start network manager in background (handles reconnections automatically!)
|
# 2. Start network manager in background (handles reconnections automatically!)
|
||||||
client.loop_start()
|
client.loop_start()
|
||||||
|
|||||||
Reference in New Issue
Block a user