Skip to content

#41 — RF Monitoring (Heatmap RSSI Temps Réel)

PLANIFIÉ

Priorité: 🔴 CRITIQUE · Type: TYPE E · Conteneur: rgz-grafana · Code: config/grafana/dashboards/rf_heatmap.json

Dépendances: #37 dashboards-grafana, #39 snmp-poller


Description

Outil de visualisation temps réel de la couverture RF (Radio-Frequency) sur l'ensemble du réseau ACCESS RGZ. Heatmap Grafana Geomap panel affichant l'intensité du signal RSSI (Received Signal Strength Indicator) pour 8 villes principales du Bénin : Cotonou, Porto-Novo, Parakou, Abomey-Calavi, Bohicon, Natitingou, Kandi et Lokossa.

Les coordonnées GPS de chaque Access Point proviennent de la table reseller_sites (latitude, longitude, city, ap_ip, last_rssi_dbm). Le RSSI est collecté toutes les 5 minutes via #39 SNMP poller dans TimescaleDB.

La heatmap utilise un dégradé de couleurs pour visualiser la qualité du signal : vert (fort >-65dBm), jaune (moyen -65 à -75dBm), rouge (faible <-75dBm). Un clic sur un point affiche l'AP associé, son revendeur, son NAS-ID, uptime SLA et données de trafic.

Architecture Interne

Dataflow:
  1. CPE Ubiquiti → SNMPv3 getters (RSSI, quality, errors)
  2. rgz-beat: app/tasks/monitoring.py → snmp_poll() toutes les 5min
  3. snmp_metrics TimescaleDB:
     └─ hypertable: time, nas_id, ap_ip, metric_name, metric_value
  4. Grafana PostgreSQL datasource:
     └─ SELECT time, city, latitude, longitude, ap_ip, last_rssi_dbm
        FROM reseller_sites rs
        LEFT JOIN snmp_metrics sm ON rs.ap_ip = sm.ap_ip
        WHERE metric_name = 'signal_strength' AND time > NOW() - INTERVAL '10 min'
  5. Geomap Panel:
     └─ Overlay points avec couleurs dégradées
     └─ Tooltip: AP name, RSSI dBm, vendor, uptime%, last_update
  6. Kibana (ElasticSearch) + Prometheus:
     └─ Alerte si RSSI < -80dBm sur >30min → SMS #61 incident

Configuration Geomap Grafana:
  - Map provider: OpenStreetMap (mapbox optionnel)
  - Marker size: RSSI en pixels (rapport linear -90 à -50 → 5 à 50px)
  - Color scheme: green (#4CAF50) > yellow (#FFC107) > red (#F44336)
  - Initial zoom: 7 (viewport Bénin complet)
  - Refresh interval: 5 minutes (sync avec SNMP)

Configuration

env
# .env pour Grafana PostgreSQL datasource
GRAFANA_DATASOURCE_PG_HOST=rgz-db
GRAFANA_DATASOURCE_PG_PORT=5432
GRAFANA_DATASOURCE_PG_DATABASE=rgz
GRAFANA_DATASOURCE_PG_USER=grafana_ro
GRAFANA_DATASOURCE_PG_PASSWORD=${PG_GRAFANA_PASSWORD}
GRAFANA_DATASOURCE_PG_SSL_MODE=require

# Prometheus scrape config pour rgz-api (expose /metrics RSSI)
PROMETHEUS_SCRAPE_INTERVAL=5m
PROMETHEUS_ALERT_RSSI_THRESHOLD=-80  # dBm
PROMETHEUS_ALERT_RSSI_DURATION=30m

# SNMP Poller config (app/config.py)
SNMP_COMMUNITY=RGZ_PUBLIC_v3
SNMP_VERSION=3
SNMP_SECURITY_LEVEL=authPriv
SNMP_USERNAME=monitoring
SNMP_AUTH_PROTOCOL=SHA
SNMP_PRIV_PROTOCOL=AES

Endpoints API

MéthodeRouteRéponse
GET/api/v1/monitoring/rssi/current{items: [{nas_id, ap_ip, city, lat, lon, rssi_dbm, timestamp, quality%}], total}
GET/api/v1/monitoring/rssi/{nas_id}/history?from=&to={items: [{timestamp, rssi_dbm, quality%}], total, pages}
GET/api/v1/monitoring/coverage-map?days=7GeoJSON {type: FeatureCollection, features: [{geometry: {type: Point, coordinates: [lon, lat]}, properties: {ap_ip, rssi, uptime%}}]}
POST/api/v1/monitoring/rssi-alertWebhook Prometheus : {status: firing, alerts: [{labels: {nas_id, city, rssi_dbm}, value}]}

Commandes Utiles

bash
# Tester collecte SNMP depuis rgz-beat
docker exec rgz-beat python -c "
from app.tasks.monitoring import snmp_poll_rssi
import asyncio
asyncio.run(snmp_poll_rssi())
"

# Vérifier métriques RSSI en DB
docker exec rgz-db psql -U rgz -d rgz -c "
SELECT nas_id, ap_ip, metric_value as rssi_dbm, time
FROM snmp_metrics
WHERE metric_name = 'signal_strength'
ORDER BY time DESC
LIMIT 10;
"

# Consulter dashboard Grafana (via Traefik)
curl -H "Authorization: Bearer ${GRAFANA_API_TOKEN}" \
  https://grafana-rgz.duckdns.org/api/dashboards/uid/rf_heatmap

# Simuler alerte Prometheus RSSI
docker exec rgz-prometheus curl -X POST \
  http://localhost:9093/api/v1/alerts \
  -d '{
    "status": "firing",
    "alerts": [{
      "labels": {"nas_id": "access_kossou", "city": "Cotonou", "rssi_dbm": "-85"},
      "annotations": {"summary": "RSSI dégradé"}
    }]
  }'

# Exporter heatmap en PNG
docker exec rgz-grafana grafana-cli admin export-dashboard \
  --dashboard-uid rf_heatmap --folder-title "RF" > rf_heatmap.json

Implémentation TODO

  • [ ] Créer hypertable TimescaleDB snmp_metrics (time BIGINT, nas_id TEXT, ap_ip TEXT, metric_name, metric_value FLOAT8)
  • [ ] Implémenter app/tasks/monitoring.py::snmp_poll_rssi() avec SNMPv3 (pysnmp library)
  • [ ] Ajouter Celery task Beat : rgz.snmp.poll every 5min queue=rgz.monitoring
  • [ ] Créer Grafana datasource PostgreSQL (pgstmt avec timerange macro)
  • [ ] Développer dashboard JSON : Geomap panel + Legend panel + Gauge (RSSI min/max)
  • [ ] Ajouter Prometheus scrape job : job_name: rssi_metricsrgz-api:8000/metrics?metric=rssi
  • [ ] Créer AlertingRule : RSSI < -80dBm pour >30min → Alertmanager
  • [ ] Implémenter webhook /api/v1/monitoring/rssi-alert → déclenche SMS #61
  • [ ] Tests : SNMP mocking, requête TimescaleDB complexe, PromQL expressions
  • [ ] Documentation : guide terrain lire heatmap, interpréter couleurs RSSI

Dernière mise à jour: 2026-02-21

PROJET MOSAÏQUE — 81 outils, 22 conteneurs, 500+ revendeurs WiFi Zone