151 lines
4.7 KiB
Python
151 lines
4.7 KiB
Python
import logging
|
|
from datetime import datetime, timezone
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.core.database import SessionLocal
|
|
from app.models import Allarme, Sito, Utente
|
|
from app.services.firebase import firebase_service
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class AlarmHandler:
|
|
"""Handler per processare gli allarmi ricevuti via MQTT e inviarli tramite FCM"""
|
|
|
|
def __init__(self):
|
|
self.db: Session = None
|
|
|
|
def _get_db(self) -> Session:
|
|
"""Ottiene una nuova sessione database"""
|
|
if self.db is None or not self.db.is_active:
|
|
self.db = SessionLocal()
|
|
return self.db
|
|
|
|
def handle_alarm(self, payload: dict):
|
|
"""
|
|
Processa un allarme ricevuto via MQTT
|
|
|
|
Steps:
|
|
1. Valida il payload
|
|
2. Salva l'allarme nel database
|
|
3. Recupera gli utenti del cliente associato al sito
|
|
4. Invia notifiche push tramite FCM
|
|
|
|
Args:
|
|
payload: Dizionario con i dati dell'allarme
|
|
"""
|
|
db = self._get_db()
|
|
|
|
try:
|
|
# 1. Validazione
|
|
sito_id = payload.get("sito_id")
|
|
if not sito_id:
|
|
logger.error("Payload senza sito_id")
|
|
return
|
|
|
|
# Verifica che il sito esista
|
|
sito = db.query(Sito).filter(Sito.id == sito_id).first()
|
|
if not sito:
|
|
logger.error(f"Sito {sito_id} non trovato")
|
|
return
|
|
|
|
# 2. Salva allarme nel database
|
|
timestamp_str = payload.get("timestamp")
|
|
timestamp = datetime.fromisoformat(
|
|
timestamp_str.replace("Z", "+00:00")
|
|
) if timestamp_str else datetime.now(timezone.utc)
|
|
|
|
allarme = Allarme(
|
|
sito_id=sito_id,
|
|
tipo=payload.get("tipo"),
|
|
severita=payload.get("severita"),
|
|
titolo=payload.get("titolo"),
|
|
descrizione=payload.get("descrizione"),
|
|
messaggio=payload.get("messaggio"),
|
|
valore_rilevato=payload.get("valore_rilevato"),
|
|
valore_soglia=payload.get("valore_soglia"),
|
|
unita_misura=payload.get("unita_misura"),
|
|
dati_sensori=payload.get("dati_sensori"),
|
|
timestamp_rilevamento=timestamp,
|
|
timestamp_notifica=datetime.now(timezone.utc),
|
|
)
|
|
|
|
db.add(allarme)
|
|
db.commit()
|
|
db.refresh(allarme)
|
|
|
|
logger.info(f"Allarme {allarme.id} salvato per sito {sito.nome}")
|
|
|
|
# 3. Recupera utenti del cliente
|
|
utenti = (
|
|
db.query(Utente)
|
|
.filter(
|
|
Utente.cliente_id == sito.cliente_id,
|
|
Utente.attivo == True,
|
|
Utente.fcm_token.isnot(None),
|
|
)
|
|
.all()
|
|
)
|
|
|
|
if not utenti:
|
|
logger.warning(
|
|
f"Nessun utente attivo con FCM token per cliente {sito.cliente_id}"
|
|
)
|
|
return
|
|
|
|
# 4. Invia notifiche push
|
|
self._send_notifications(allarme, sito, utenti)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Errore nel processamento dell'allarme: {e}")
|
|
db.rollback()
|
|
finally:
|
|
db.close()
|
|
|
|
def _send_notifications(self, allarme: Allarme, sito: Sito, utenti: list[Utente]):
|
|
"""Invia notifiche push agli utenti"""
|
|
|
|
# Determina priorità basata sulla severità
|
|
priority = "high" if allarme.severita == "critical" else "normal"
|
|
|
|
# Prepara dati per la notifica
|
|
notification_data = {
|
|
"alarm_id": str(allarme.id),
|
|
"sito_id": str(sito.id),
|
|
"sito_nome": sito.nome,
|
|
"tipo": allarme.tipo,
|
|
"severita": allarme.severita,
|
|
"timestamp": allarme.timestamp_rilevamento.isoformat(),
|
|
}
|
|
|
|
# Prepara titolo e messaggio
|
|
title = f"{allarme.severita.upper()}: {sito.nome}"
|
|
body = allarme.titolo or allarme.descrizione or "Nuovo allarme rilevato"
|
|
|
|
# Raccogli tutti i token
|
|
tokens = [u.fcm_token for u in utenti if u.fcm_token]
|
|
|
|
if not tokens:
|
|
logger.warning("Nessun token FCM valido trovato")
|
|
return
|
|
|
|
logger.info(f"Invio notifica a {len(tokens)} dispositivi")
|
|
|
|
# Invia notifica multicast
|
|
result = firebase_service.send_multicast(
|
|
tokens=tokens,
|
|
title=title,
|
|
body=body,
|
|
data=notification_data,
|
|
priority=priority,
|
|
)
|
|
|
|
logger.info(
|
|
f"Notifiche inviate per allarme {allarme.id}: "
|
|
f"{result['success']} successi, {result['failure']} fallimenti"
|
|
)
|
|
|
|
|
|
# Singleton instance
|
|
alarm_handler = AlarmHandler()
|