16 Commits

Author SHA1 Message Date
c0a8929084 Fix: Gérer les OID invalides avec un avertissement
Plutôt que de générer une erreur lorsqu'un OID SNMP renvoie une valeur vide ou non-convertible, le système émet désormais un simple avertissement.

Cela évite de polluer les logs d'erreurs pour des cas non-bloquants, comme un port débranché qui renvoie une chaîne vide au lieu d'un entier.
2026-02-15 21:12:15 +01:00
6c7248f08d Mise à jour de la documentation (humaine + IA) 2025-11-06 17:08:37 +01:00
7888999ec3 feat: Ajoute la transformation de valeur par opération mathématique
Cette modification introduit la possibilité d'appliquer une opération mathématique simple (addition, soustraction, multiplication, division) sur les valeurs SNMP récupérées.

L'utilisateur peut désormais définir une clé 'operation' dans la configuration d'un OID (par exemple, 'value / 1000') pour normaliser les données avant leur envoi à MQTT. La documentation dans `config.yaml` a été mise à jour avec un exemple.
2025-11-06 17:05:20 +01:00
ac476df7ce fix: les capteurs 'data_size' ont l'attribut 'total_increasing' 2025-09-09 22:33:37 +02:00
f48a9d1417 Aide au codage 2025-09-09 22:32:57 +02:00
b7331faba0 logging INFO par défaut 2025-08-24 16:57:21 +02:00
3875f4501f Config example 2025-08-24 16:47:23 +02:00
7fec5c5049 DEBUG c'est quand même très verbeux -> passage à INFO:x 2025-08-24 16:39:08 +02:00
2309ef1deb Mise à jour avec la nouvelle API (V2) de Paho MQTT 2025-08-24 16:37:25 +02:00
75f8c6a637 Remove WARP.md from repo 2025-08-24 16:27:18 +02:00
0d54632c52 Mise à jour en lien avec les changements dans PySNMP 2025-08-24 16:25:35 +02:00
5b670c4708 Fix UdpTransportTarget 2025-08-24 15:44:00 +02:00
85a14f4fa0 Update PySNMP (Slim is now deprecated) 2025-08-24 15:36:26 +02:00
e4e79a34a9 Merge branch 'ha-mqtt-autodiscovery'
Fusion du README pour l'autodiscovery HA
2025-08-24 15:23:39 +02:00
ffd86281ef Merge branch 'multithreading-for-multiple-devices'
Fusion du README pour le MT
2025-08-24 15:23:10 +02:00
7199432169 Informations sur le multithreading 2025-08-24 15:19:24 +02:00
7 changed files with 229 additions and 188 deletions

1
.gitignore vendored
View File

@@ -172,3 +172,4 @@ cython_debug/
#.idea/ #.idea/
WARP.md

79
GEMINI.md Normal file
View File

@@ -0,0 +1,79 @@
# Fichier d'aide pour Gemini
Ce document fournit un résumé concis du projet `snmp2mqtt` pour aider au développement et à la maintenance assistés par l'IA.
## 1. Objectif du Projet
Le projet `snmp2mqtt` est une passerelle écrite en Python qui a pour but de :
1. **Interroger** des équipements réseau (routeurs, switchs, etc.) via le protocole **SNMP**.
2. **Récupérer** des métriques spécifiques (trafic, statut des ports, etc.) définies par des OIDs.
3. **Publier** ces données sur un broker **MQTT**.
4. **S'intégrer automatiquement** avec **Home Assistant** grâce au mécanisme de "MQTT Discovery", permettant de créer des capteurs sans configuration manuelle côté Home Assistant.
## 2. Architecture et Technologies
- **Langage** : Python 3
- **Dépendances principales** (`requirements.txt`) :
- `pysnmp>=7.0.0` : Pour la communication SNMP asynchrone.
- `paho-mqtt>=2.0.0` : Pour la communication avec le broker MQTT.
- `PyYAML>=6.0.0` : Pour le parsing du fichier de configuration.
- **Configuration** : Un unique fichier `config.yaml` centralise tous les paramètres (MQTT, appareils, OIDs).
- **Exécution** : Le script utilise le multi-threading. Un thread est démarré pour chaque appareil défini dans la configuration, ce qui permet une surveillance parallèle et isolée.
## 3. Structure du Code (`snmp2mqtt.py`)
Le script principal est organisé de la manière suivante :
1. **`main()`** : Point d'entrée. Il parse les arguments (`--config`), charge la configuration et appelle `process_devices()`.
2. **`process_devices(config)`** :
- Orchestre le lancement des threads.
- Crée et démarre une instance de `DeviceMonitorThread` pour chaque appareil.
- Gère l'arrêt propre (`graceful shutdown`) en attendant que tous les threads se terminent.
3. **`DeviceMonitorThread(threading.Thread)`** :
- Classe qui encapsule la logique de surveillance pour un seul appareil.
- `run()` : méthode principale du thread.
- Établit la connexion MQTT.
- Publie la configuration de découverte automatique pour Home Assistant (une seule fois au démarrage).
- Entre dans une boucle infinie qui :
- Appelle `get_snmp()` pour récupérer les données.
- Publie l'état des capteurs et le statut de disponibilité (`online`/`offline`) sur MQTT.
- Attend un intervalle (`sleep_interval`) avant la prochaine interrogation.
4. **`get_snmp(req)`** :
- Fonction `async` qui utilise `pysnmp` pour exécuter les requêtes `GET` SNMP pour tous les OIDs d'un appareil.
- Traite la clé optionnelle `operation` pour appliquer une transformation mathématique.
- Retourne un dictionnaire contenant les valeurs formatées.
5. **Fonctions de configuration et MQTT** :
- `load_config()` : Charge et valide le fichier `config.yaml`.
- `connect_mqtt()` : Initialise le client MQTT.
- `publish()` : Wrapper pour publier les messages MQTT.
- `publish_ha_autodiscovery_config()` : Construit et publie les messages de configuration pour Home Assistant MQTT Discovery.
- `apply_operation(value, operation_str)` : Fonction d'aide qui applique de manière sécurisée une opération mathématique simple à une valeur.
## 4. Flux de Données
```
[Appareil SNMP] <--- (Requête SNMP GET) --- [snmp2mqtt.py / Thread]
|
| (Réponse SNMP)
v
[snmp2mqtt.py / Thread] --- (Publication MQTT) ---> [Broker MQTT]
|
| (MQTT Discovery & State)
v
[Home Assistant]
```
## 5. Comment développer
- **Environnement** :
1. Créer un environnement virtuel : `python3 -m venv .venv`
2. Activer l'environnement : `source .venv/bin/activate`
3. Installer les dépendances : `pip install -r requirements.txt`
- **Configuration** :
- Copier et modifier `config.yaml` pour pointer vers un broker MQTT de test et un appareil SNMP accessible.
- **Lancement** :
- `python snmp2mqtt.py --config config.yaml`
- **Points clés à modifier** :
- Pour ajouter une nouvelle fonctionnalité à un capteur Home Assistant, modifier `create_ha_sensor_config()`.
- Pour changer la logique de récupération SNMP, modifier `get_snmp()`.
- Pour ajouter de nouveaux paramètres de configuration, mettre à jour `load_config()` pour la validation.

View File

@@ -10,12 +10,13 @@ Passerelle SNMP vers MQTT pour l'intégration Home Assistant. Ce script Python s
-**SNMP asynchrone** : Requêtes SNMP non-bloquantes pour de meilleures performances -**SNMP asynchrone** : Requêtes SNMP non-bloquantes pour de meilleures performances
- 🔄 **Surveillance en temps réel** : Mise à jour continue des métriques réseau - 🔄 **Surveillance en temps réel** : Mise à jour continue des métriques réseau
- 📊 **Métriques réseau** : Trafic entrant/sortant et statut des interfaces - 📊 **Métriques réseau** : Trafic entrant/sortant et statut des interfaces
- 🔢 **Transformation de données** : Appliquez des opérations mathématiques simples (division, multiplication...) pour normaliser les valeurs.
## Architecture ## Architecture
### Composants principaux ### Composants principaux
- **Client SNMP** : Utilise `pysnmp.hlapi.asyncio.slim` pour la récupération asynchrone des données SNMP - **Client SNMP** : Utilise `pysnmp.hlapi.asyncio` (version 7.x) pour la récupération asynchrone des données SNMP avec `get_cmd`, `SnmpEngine` et `UdpTransportTarget`
- **Publisher MQTT** : Utilise `paho.mqtt.client` pour publier les données vers un broker MQTT - **Publisher MQTT** : Utilise `paho.mqtt.client` pour publier les données vers un broker MQTT
- **Intégration Home Assistant** : Génère la configuration de découverte automatique compatible avec Home Assistant MQTT Discovery - **Intégration Home Assistant** : Génère la configuration de découverte automatique compatible avec Home Assistant MQTT Discovery
- **Traitement des données** : Convertit les valeurs des OID SNMP vers les types appropriés (int, bool) pour les capteurs Home Assistant - **Traitement des données** : Convertit les valeurs des OID SNMP vers les types appropriés (int, bool) pour les capteurs Home Assistant
@@ -28,6 +29,16 @@ Passerelle SNMP vers MQTT pour l'intégration Home Assistant. Ce script Python s
- Accès réseau aux équipements SNMP à surveiller - Accès réseau aux équipements SNMP à surveiller
- Broker MQTT accessible - Broker MQTT accessible
### Dépendances principales
- **pysnmp >= 7.0.0** : Bibliothèque SNMP avec nouvelle API asynchrone
- **paho-mqtt >= 2.0.0** : Client MQTT pour la communication avec le broker
- **PyYAML >= 6.0.0** : Parsing des fichiers de configuration YAML
⚠️ **Notes importantes sur les versions** :
- **pysnmp 7.x** : Changements d'API incompatibles avec les versions 6.x et antérieures. L'ancienne classe `Slim` a été supprimée au profit de `get_cmd()` avec des objets `SnmpEngine`, `UdpTransportTarget`, etc.
- **paho-mqtt 2.x** : Nouvelle API de callbacks (VERSION2) qui remplace l'ancienne API deprecated (VERSION1). Les signatures des callbacks ont changé.
### Configuration de l'environnement ### Configuration de l'environnement
```bash ```bash
@@ -109,6 +120,7 @@ Chaque OID dans la liste `oids` doit contenir :
| `HA_device_class` | string | ✅ | Classe d'équipement Home Assistant | | `HA_device_class` | string | ✅ | Classe d'équipement Home Assistant |
| `HA_platform` | string | ✅ | Plateforme Home Assistant ("sensor", "binary_sensor") | | `HA_platform` | string | ✅ | Plateforme Home Assistant ("sensor", "binary_sensor") |
| `HA_unit` | string | ❌ | Unité de mesure pour le capteur | | `HA_unit` | string | ❌ | Unité de mesure pour le capteur |
| `operation` | string | ❌ | Opération mathématique à appliquer (ex: "value / 1000") |
### Classes d'équipements Home Assistant courantes ### Classes d'équipements Home Assistant courantes
@@ -328,6 +340,15 @@ devices:
HA_device_class: "connectivity" HA_device_class: "connectivity"
HA_platform: "binary_sensor" HA_platform: "binary_sensor"
# Exemple avec transformation de valeur (température en millidegrés -> degrés)
# - name: "temperature"
# oid: ".1.3.6.1.4.1.14988.1.1.6.1.0" # OID pour la température sur MikroTik
# type: "int"
# operation: "value / 1000"
# HA_device_class: "temperature"
# HA_platform: "sensor"
# HA_unit: "°C"
# Switch réseau # Switch réseau
switch_bureau: switch_bureau:
ip: "192.168.10.5" ip: "192.168.10.5"
@@ -361,6 +382,16 @@ devices:
- Vérifiez que MQTT Discovery est activé dans Home Assistant - Vérifiez que MQTT Discovery est activé dans Home Assistant
- Surveillez les logs MQTT avec `mosquitto_sub` - Surveillez les logs MQTT avec `mosquitto_sub`
5. **Erreurs liées à PySNMP**
- **"ModuleNotFoundError: No module named 'pysnmp.hlapi.asyncio.slim'"** : Vous utilisez une version pysnmp 6.x. Mettez à jour vers >= 7.0.0
- **"Please call .create() to construct UdpTransportTarget object"** : Erreur corrigée dans cette version, utilisez `pip install -r requirements.txt`
- **Erreurs d'importation SNMP** : Assurez-vous d'avoir pysnmp 7.x avec `pip show pysnmp`
6. **Erreurs liées à Paho MQTT**
- **"DeprecationWarning: Callback API version 1 is deprecated"** : Vous utilisez une version paho-mqtt < 2.0. Mettez à jour vers >= 2.0.0
- **Erreurs de callback MQTT** : La nouvelle API VERSION2 change la signature des callbacks (ex: `on_connect` reçoit maintenant 5 paramètres)
- **Vérification version** : `pip show paho-mqtt` pour confirmer la version installée
### Commandes de test utiles ### Commandes de test utiles
```bash ```bash
@@ -438,7 +469,7 @@ Chaque thread est clairement identifié dans les logs :
## Logs et debugging ## Logs et debugging
Le script utilise le module `logging` de Python avec le niveau DEBUG par défaut. Les logs incluent : Le script utilise le module `logging` de Python avec le niveau INFO par défaut. Les logs incluent :
- Chargement de la configuration - Chargement de la configuration
- Connexions MQTT - Connexions MQTT

112
WARP.md
View File

@@ -1,112 +0,0 @@
# WARP.md
This file provides guidance to WARP (warp.dev) when working with code in this repository.
## Project Overview
snmp2mqtt is a Python script that bridges SNMP network device monitoring with MQTT messaging for Home Assistant integration. It specifically monitors a MikroTik router (Hex) and publishes network interface statistics and status information to MQTT topics for Home Assistant discovery.
## Architecture
### Core Components
- **SNMP Client**: Uses `pysnmp.hlapi.asyncio.slim` for asynchronous SNMP data retrieval from network devices
- **MQTT Publisher**: Uses `paho.mqtt.client` to publish data to an MQTT broker
- **Home Assistant Integration**: Generates device discovery configuration compatible with Home Assistant MQTT Discovery
- **Data Processing**: Converts SNMP OID values to appropriate data types (int, bool) for Home Assistant sensors
### Key Functions
- `get_snmp(req)`: Asynchronously retrieves SNMP data from configured OIDs
- `connect_mqtt(mqtt_config)`: Establishes MQTT broker connection
- `publish(topic, client, data, retain, qos)`: Publishes JSON data to MQTT topics
- `ha_create_config(req)`: Generates Home Assistant device discovery configuration
- `send_to_mqtt()`: Main loop that continuously publishes config and state data
### Configuration Structure
The script uses two main configuration dictionaries:
- `req`: Defines the target device, SNMP community, and monitored OIDs with Home Assistant metadata
- `mqtt_config`: MQTT broker connection parameters
## Common Development Commands
### Environment Setup
```bash
# Activate virtual environment
source bin/activate
# Install dependencies (if needed)
pip install pysnmp paho-mqtt
# Check installed packages
pip list
```
### Running the Application
```bash
# Run the main script
python snmp2mqtt.py
# Run with Python 3 explicitly
python3 snmp2mqtt.py
# Run from virtual environment
./bin/python snmp2mqtt.py
```
### Development and Testing
```bash
# Test SNMP connectivity to device
# (Manual SNMP walk example)
snmpwalk -v2c -c public 192.168.10.2 1.3.6.1.2.1.2.2.1.10
# Monitor MQTT messages (if mosquitto-clients available)
mosquitto_sub -h 192.168.10.202 -u snmp2mqtt -P 'snmp_2_MQTT' -t 'homeassistant/device/+/config'
mosquitto_sub -h 192.168.10.202 -u snmp2mqtt -P 'snmp_2_MQTT' -t 'SNMP/+/state'
# Check network connectivity
ping 192.168.10.2
ping 192.168.10.202
```
## Configuration Notes
### Device Configuration
- Hardcoded to monitor MikroTik Hex router at IP `192.168.10.2`
- SNMP community: `public`
- Monitors interfaces: Starlink (index 1), LAN bridge (index 6), VPN (index 12)
### MQTT Configuration
- Broker: `192.168.10.202:1883`
- Credentials: `snmp2mqtt` / `snmp_2_MQTT`
- Config topic: `homeassistant/device/{device_id}/config`
- State topic: `SNMP/{device_name}/state`
### Monitored Metrics
For each interface:
- **Incoming bytes** (`oid: .1.3.6.1.2.1.2.2.1.10.X`) - Published as data_size sensor
- **Outgoing bytes** (`oid: .1.3.6.1.2.1.2.2.1.16.X`) - Published as data_size sensor
- **Interface status** (`oid: .1.3.6.1.2.1.2.2.1.8.X`) - Published as connectivity binary_sensor
## Customization Points
### Adding New Devices
1. Create new `req` configuration dictionary with device details
2. Update `mqtt_config` if different broker needed
3. Configure appropriate SNMP OIDs for the device type
### Adding New OIDs
Each OID entry requires:
- `name`: Unique identifier for Home Assistant
- `oid`: SNMP Object Identifier
- `type`: Python type conversion (int, bool)
- `HA_platform`: Home Assistant platform (sensor, binary_sensor)
- `HA_device_class`: Device class for proper Home Assistant categorization
- `HA_unit`: (optional) Unit of measurement
### Home Assistant Integration
The script automatically creates Home Assistant MQTT Discovery configuration with:
- Device identification and grouping
- Sensor types and units appropriate for network monitoring
- Value templates for JSON data extraction

View File

@@ -3,10 +3,10 @@
# MQTT Broker Configuration # MQTT Broker Configuration
mqtt: mqtt:
broker: "192.168.10.202" broker: "IP or FQDN"
port: 1883 port: 1883
user: "snmp2mqtt" user: "USER"
password: "snmp_2_MQTT" password: "PASSWORD"
# Optional: Sleep interval between SNMP polls (default: 2 seconds) # Optional: Sleep interval between SNMP polls (default: 2 seconds)
sleep_interval: 2 sleep_interval: 2
@@ -16,72 +16,41 @@ sleep_interval: 2
devices: devices:
# Device name (used for MQTT topics and Home Assistant device identification) # Device name (used for MQTT topics and Home Assistant device identification)
mikrotik_hex: mikrotik_hex:
ip: "192.168.10.2" ip: "IP"
snmp_community: "public" snmp_community: "public"
oids: oids:
# Starlink VPN interface (interface index 12) # example interface index 1
- name: "stln_vpn_in" - name: "if1_in"
oid: ".1.3.6.1.2.1.2.2.1.10.12"
type: "int"
HA_device_class: "data_size"
HA_platform: "sensor"
HA_unit: "bit"
- name: "stlon_vpn_out"
oid: ".1.3.6.1.2.1.2.2.1.16.12"
type: "int"
HA_device_class: "data_size"
HA_platform: "sensor"
HA_unit: "bit"
- name: "stln_vpn_status"
oid: ".1.3.6.1.2.1.2.2.1.8.12"
type: "bool"
HA_device_class: "connectivity"
HA_platform: "binary_sensor"
# LAN Bridge interface (interface index 6)
- name: "lan_bridge_in"
oid: ".1.3.6.1.2.1.2.2.1.10.6"
type: "int"
HA_device_class: "data_size"
HA_platform: "sensor"
HA_unit: "bit"
- name: "lan_bridge_out"
oid: ".1.3.6.1.2.1.2.2.1.16.6"
type: "int"
HA_device_class: "data_size"
HA_platform: "sensor"
HA_unit: "bit"
- name: "lan_bridge_status"
oid: ".1.3.6.1.2.1.2.2.1.8.6"
type: "bool"
HA_device_class: "connectivity"
HA_platform: "binary_sensor"
# Starlink interface (interface index 1)
- name: "starlink_in"
oid: ".1.3.6.1.2.1.2.2.1.10.1" oid: ".1.3.6.1.2.1.2.2.1.10.1"
type: "int" type: "int"
HA_device_class: "data_size" HA_device_class: "data_size"
HA_platform: "sensor" HA_platform: "sensor"
HA_unit: "bit" HA_unit: "bit"
- name: "starlink_out" - name: "if1_out"
oid: ".1.3.6.1.2.1.2.2.1.16.1" oid: ".1.3.6.1.2.1.2.2.1.16.1"
type: "int" type: "int"
HA_device_class: "data_size" HA_device_class: "data_size"
HA_platform: "sensor" HA_platform: "sensor"
HA_unit: "bit" HA_unit: "bit"
- name: "starlink_status" - name: "if1_status"
oid: ".1.3.6.1.2.1.2.2.1.8.1" oid: ".1.3.6.1.2.1.2.2.1.8.1"
type: "bool" type: "bool"
HA_device_class: "connectivity" HA_device_class: "connectivity"
HA_platform: "binary_sensor" HA_platform: "binary_sensor"
# Example of a temperature sensor that returns the value in millidegrees.
# The 'operation' key allows performing a simple calculation.
# The placeholder 'value' will be replaced by the SNMP value.
# - name: "temperature"
# oid: ".1.3.6.1.4.1.XXXX.1.1.1.5.1.3.1" # Example OID
# type: "int"
# operation: "value / 1000"
# HA_device_class: "temperature"
# HA_platform: "sensor"
# HA_unit: "°C"
# OID Configuration Reference: # OID Configuration Reference:
# - name: Unique identifier for this metric (used in MQTT topics and Home Assistant) # - name: Unique identifier for this metric (used in MQTT topics and Home Assistant)
# - oid: SNMP Object Identifier # - oid: SNMP Object Identifier

View File

@@ -2,10 +2,12 @@
# Install with: pip install -r requirements.txt # Install with: pip install -r requirements.txt
# SNMP library for asynchronous SNMP operations # SNMP library for asynchronous SNMP operations
pysnmp>=6.0.0 # Note: pysnmp 7.x uses a new API structure (no more Slim class)
pysnmp>=7.0.0
# MQTT client library for connecting to MQTT brokers # MQTT client library for connecting to MQTT brokers
paho-mqtt>=1.6.0 # Note: paho-mqtt 2.x uses a new callback API (VERSION2) instead of the deprecated VERSION1
paho-mqtt>=2.0.0
# YAML configuration file parsing # YAML configuration file parsing
PyYAML>=6.0.0 PyYAML>=6.0.0

View File

@@ -1,7 +1,9 @@
#!/bin/env python3 #!/bin/env python3
import asyncio import asyncio
from pysnmp.hlapi.asyncio.slim import Slim from pysnmp.hlapi.asyncio import (
from pysnmp.smi.rfc1902 import ObjectIdentity, ObjectType get_cmd, CommunityData, UdpTransportTarget, ContextData,
SnmpEngine, ObjectIdentity, ObjectType
)
import logging import logging
import random import random
from paho.mqtt import client as mqtt_client from paho.mqtt import client as mqtt_client
@@ -17,7 +19,7 @@ import time
logging.basicConfig( logging.basicConfig(
format='(%(levelname)s) [%(threadName)s] %(message)s', format='(%(levelname)s) [%(threadName)s] %(message)s',
level=logging.DEBUG level=logging.INFO
) )
# Global shutdown flag # Global shutdown flag
@@ -177,12 +179,14 @@ def load_config(config_path):
def connect_mqtt(mqtt_config): def connect_mqtt(mqtt_config):
def on_connect(client, userdata, flags, rc): def on_connect(client, userdata, connect_flags, reason_code, properties):
if rc == 0: if reason_code == 0:
print("Connected to MQTT Broker!") logging.info("Connected to MQTT Broker!")
else: else:
print("Failed to connect, return code {rc}") logging.error(f"Failed to connect to MQTT Broker, reason code: {reason_code}")
client = mqtt_client.Client()
# Use the new callback API version 2
client = mqtt_client.Client(callback_api_version=mqtt_client.CallbackAPIVersion.VERSION2)
client.username_pw_set(mqtt_config["user"], mqtt_config["password"]) client.username_pw_set(mqtt_config["user"], mqtt_config["password"])
client.on_connect = on_connect client.on_connect = on_connect
client.connect(mqtt_config['broker'], mqtt_config['port']) client.connect(mqtt_config['broker'], mqtt_config['port'])
@@ -203,37 +207,101 @@ def publish(topic, client, data, retain, qos):
logging.error(f"Failed to send message to topic {topic}") logging.error(f"Failed to send message to topic {topic}")
def apply_operation(value, operation_str):
"""
Applies a simple mathematical operation to a value.
e.g., operation_str = "value / 1000"
"""
if 'value' not in operation_str:
logging.error(f"Invalid operation string: 'value' placeholder missing in '{operation_str}'")
return value
expression = operation_str.replace('value', str(value))
try:
parts = expression.split()
if len(parts) != 3:
logging.error(f"Invalid operation format: '{operation_str}'. Expected 'value <operator> <operand>'")
return value
val = float(parts[0])
operator = parts[1]
operand = float(parts[2])
if operator == '+':
return val + operand
elif operator == '-':
return val - operand
elif operator == '*':
return val * operand
elif operator == '/':
if operand == 0:
logging.warning(f"Attempted division by zero in operation: {operation_str}")
return value
return val / operand
else:
logging.error(f"Unsupported operator: '{operator}' in '{operation_str}'")
return value
except (ValueError, IndexError) as e:
logging.error(f"Could not parse operation string: '{operation_str}'. Error: {e}")
return value
async def get_snmp(req): async def get_snmp(req):
"""Asynchronously retrieve SNMP data from device using new pysnmp API"""
data = {} data = {}
# Create SNMP engine and transport target
snmpEngine = SnmpEngine()
authData = CommunityData(req["snmp_community"])
transportTarget = await UdpTransportTarget.create((req["ip"], 161))
contextData = ContextData()
for oid in req["oids"]: for oid in req["oids"]:
with Slim(1) as slim: try:
errorIndication, errorStatus, errorIndex, varBinds = await slim.get( # Perform async SNMP GET operation
req["snmp_community"], errorIndication, errorStatus, errorIndex, varBinds = await get_cmd(
req["ip"], snmpEngine,
161, authData,
ObjectType(ObjectIdentity(oid["oid"])), transportTarget,
contextData,
ObjectType(ObjectIdentity(oid["oid"]))
) )
if errorIndication: if errorIndication:
logging.error(errorIndication) logging.error(f"{req['device_name']} SNMP error indication: {errorIndication}")
continue
elif errorStatus: elif errorStatus:
logging.error( logging.error(
"{} at {}".format( f"{req['device_name']} SNMP error status: {errorStatus.prettyPrint()} at {errorIndex and varBinds[int(errorIndex) - 1][0] or '?'}"
errorStatus.prettyPrint(),
errorIndex and varBinds[int(errorIndex) - 1][0] or "?",
)
) )
continue
else: else:
for varBind in varBinds: for varBind in varBinds:
logging.debug(f"{req['device_name']} {oid['name']} => {oid['type'](varBind[1])}") logging.debug(f"{req['device_name']} {oid['name']} => {oid['type'](varBind[1])}")
# Cast to the right type
value = oid['type'](varBind[1])
# Apply operation if defined
if 'operation' in oid:
value = apply_operation(value, oid['operation'])
if oid['type'] == bool: if oid['type'] == bool:
if bool(varBind[1]): if bool(value):
data.update({oid["name"]: "ON"}) data.update({oid["name"]: "ON"})
else: else:
data.update({oid["name"]: "OFF"}) data.update({oid["name"]: "OFF"})
else: else:
data.update({oid["name"]: oid["type"](varBind[1])}) data.update({oid["name"]: value})
logging.debug(f"JSON : {json.dumps(data)}") except ValueError as e:
logging.warning(f"{req['device_name']} OID {oid['oid']} ({oid['name']}) returned an invalid value: {e}")
continue
except Exception as e:
logging.error(f"{req['device_name']} Exception getting OID {oid['oid']}: {e}")
continue
logging.debug(f"{req['device_name']} JSON : {json.dumps(data)}")
return data return data
@@ -269,6 +337,9 @@ def create_ha_sensor_config(req, oid):
# Add device class if specified # Add device class if specified
if 'HA_device_class' in oid: if 'HA_device_class' in oid:
config['device_class'] = oid['HA_device_class'] config['device_class'] = oid['HA_device_class']
# Add state_class for total_increasing counters like data size
if oid['HA_device_class'] == 'data_size':
config['state_class'] = 'total_increasing'
# Add unit of measurement if specified # Add unit of measurement if specified
if 'HA_unit' in oid: if 'HA_unit' in oid: