Compare commits
8 Commits
d37b5ae636
...
config-fil
Author | SHA1 | Date | |
---|---|---|---|
ca4ad8ed06
|
|||
819ff2ed97
|
|||
0391f91809
|
|||
3796f500e7
|
|||
ec1dad8e8c
|
|||
565edd6adc
|
|||
61c825ab22
|
|||
e00bcdf49c
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -136,6 +136,9 @@ venv/
|
|||||||
ENV/
|
ENV/
|
||||||
env.bak/
|
env.bak/
|
||||||
venv.bak/
|
venv.bak/
|
||||||
|
bin/
|
||||||
|
lib64
|
||||||
|
pyvenv.cfg
|
||||||
|
|
||||||
# Spyder project settings
|
# Spyder project settings
|
||||||
.spyderproject
|
.spyderproject
|
||||||
@@ -168,3 +171,4 @@ cython_debug/
|
|||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
#.idea/
|
||||||
|
|
||||||
|
|
||||||
|
9
LICENSE
9
LICENSE
@@ -1,7 +1,14 @@
|
|||||||
MIT License
|
MIT License (Expat)
|
||||||
|
|
||||||
Copyright (c) 2025 AntoineVe
|
Copyright (c) 2025 AntoineVe
|
||||||
|
|
||||||
|
La présente autorise, de façon libre et gratuite, à toute personne obtenant une copie de ce programme et des fichiers de documentation associés (le "Programme"), de distribuer le Programme sans restriction, y compris sans limitation des droits d'utiliser, copier, modifier, fusionner, publier, distribuer, sous-autoriser ou vendre des copies du Programme, et de permettre aux personnes à qui le Programme est fourni d'en faire autant, aux conditions suivantes.
|
||||||
|
|
||||||
|
Le copyright précédent et cette autorisation doivent être distribués dans toute copie entière ou substantielle de ce Programme.
|
||||||
|
|
||||||
|
Le Programme est fourni en l'état, sans garantie d'aucune sorte, explicite ou implicite, y compris les garanties de commercialisation ou d'adaptation dans un but particulier et l'absence de contrefaçon. En aucun cas les auteurs ou ayants droit ne seront tenus responsables de réclamations, dommages ou autres, que ce soit dans une action de nature contractuelle, préjudiciable ou autres façons, découlant de, hors ou en connexion avec le Programme ou l'utilisation ou autres modifications du Programme.
|
||||||
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
112
WARP.md
Normal file
112
WARP.md
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# 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
|
111
config.yaml
Normal file
111
config.yaml
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# SNMP2MQTT Configuration File
|
||||||
|
# This file contains the configuration for the SNMP to MQTT bridge
|
||||||
|
|
||||||
|
# MQTT Broker Configuration
|
||||||
|
mqtt:
|
||||||
|
broker: "192.168.10.202"
|
||||||
|
port: 1883
|
||||||
|
user: "snmp2mqtt"
|
||||||
|
password: "snmp_2_MQTT"
|
||||||
|
|
||||||
|
# Optional: Sleep interval between SNMP polls (default: 2 seconds)
|
||||||
|
sleep_interval: 2
|
||||||
|
|
||||||
|
# Device Configurations
|
||||||
|
# You can define multiple devices here. Each device will be monitored independently.
|
||||||
|
devices:
|
||||||
|
# Device name (used for MQTT topics and Home Assistant device identification)
|
||||||
|
mikrotik_hex:
|
||||||
|
ip: "192.168.10.2"
|
||||||
|
snmp_community: "public"
|
||||||
|
oids:
|
||||||
|
# Starlink VPN interface (interface index 12)
|
||||||
|
- name: "stln_vpn_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"
|
||||||
|
type: "int"
|
||||||
|
HA_device_class: "data_size"
|
||||||
|
HA_platform: "sensor"
|
||||||
|
HA_unit: "bit"
|
||||||
|
|
||||||
|
- name: "starlink_out"
|
||||||
|
oid: ".1.3.6.1.2.1.2.2.1.16.1"
|
||||||
|
type: "int"
|
||||||
|
HA_device_class: "data_size"
|
||||||
|
HA_platform: "sensor"
|
||||||
|
HA_unit: "bit"
|
||||||
|
|
||||||
|
- name: "starlink_status"
|
||||||
|
oid: ".1.3.6.1.2.1.2.2.1.8.1"
|
||||||
|
type: "bool"
|
||||||
|
HA_device_class: "connectivity"
|
||||||
|
HA_platform: "binary_sensor"
|
||||||
|
|
||||||
|
# Example of how to add another device:
|
||||||
|
# another_device:
|
||||||
|
# ip: "192.168.10.3"
|
||||||
|
# snmp_community: "public"
|
||||||
|
# oids:
|
||||||
|
# - name: "cpu_usage"
|
||||||
|
# oid: ".1.3.6.1.4.1.14988.1.1.3.14.0" # MikroTik CPU usage
|
||||||
|
# type: "int"
|
||||||
|
# HA_device_class: "power_factor"
|
||||||
|
# HA_platform: "sensor"
|
||||||
|
# HA_unit: "%"
|
||||||
|
|
||||||
|
# OID Configuration Reference:
|
||||||
|
# - name: Unique identifier for this metric (used in MQTT topics and Home Assistant)
|
||||||
|
# - oid: SNMP Object Identifier
|
||||||
|
# - type: Python type for value conversion ("int", "bool", "str")
|
||||||
|
# - HA_device_class: Home Assistant device class for proper icon/categorization
|
||||||
|
# Common classes: data_size, connectivity, power_factor, temperature, etc.
|
||||||
|
# - HA_platform: Home Assistant platform type ("sensor", "binary_sensor")
|
||||||
|
# - HA_unit: (optional) Unit of measurement for the sensor
|
||||||
|
# Common units: "bit", "byte", "%", "°C", "°F", etc.
|
||||||
|
|
||||||
|
# Common SNMP OIDs for network interfaces:
|
||||||
|
# - .1.3.6.1.2.1.2.2.1.10.X = Incoming bytes on interface X
|
||||||
|
# - .1.3.6.1.2.1.2.2.1.16.X = Outgoing bytes on interface X
|
||||||
|
# - .1.3.6.1.2.1.2.2.1.8.X = Interface operational status (1=up, 2=down)
|
||||||
|
# - .1.3.6.1.2.1.2.2.1.2.X = Interface description
|
11
requirements.txt
Normal file
11
requirements.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# SNMP2MQTT Python Dependencies
|
||||||
|
# Install with: pip install -r requirements.txt
|
||||||
|
|
||||||
|
# SNMP library for asynchronous SNMP operations
|
||||||
|
pysnmp>=6.0.0
|
||||||
|
|
||||||
|
# MQTT client library for connecting to MQTT brokers
|
||||||
|
paho-mqtt>=1.6.0
|
||||||
|
|
||||||
|
# YAML configuration file parsing
|
||||||
|
PyYAML>=6.0.0
|
260
snmp2mqtt.py
Executable file
260
snmp2mqtt.py
Executable file
@@ -0,0 +1,260 @@
|
|||||||
|
#!/bin/env python3
|
||||||
|
import asyncio
|
||||||
|
from pysnmp.hlapi.asyncio.slim import Slim
|
||||||
|
from pysnmp.smi.rfc1902 import ObjectIdentity, ObjectType
|
||||||
|
import logging
|
||||||
|
import random
|
||||||
|
from paho.mqtt import client as mqtt_client
|
||||||
|
import json
|
||||||
|
from time import sleep
|
||||||
|
import yaml
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
format='(%(levelname)s) %(message)s',
|
||||||
|
level=logging.DEBUG
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_arguments():
|
||||||
|
"""Parse command line arguments"""
|
||||||
|
parser = argparse.ArgumentParser(description='SNMP to MQTT bridge for Home Assistant')
|
||||||
|
parser.add_argument('--config', '-c', required=True,
|
||||||
|
help='Path to YAML configuration file')
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(config_path):
|
||||||
|
"""Load and validate YAML configuration file"""
|
||||||
|
if not os.path.exists(config_path):
|
||||||
|
logging.error(f"Configuration file not found: {config_path}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(config_path, 'r') as file:
|
||||||
|
config = yaml.safe_load(file)
|
||||||
|
except yaml.YAMLError as e:
|
||||||
|
logging.error(f"Error parsing YAML configuration: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error reading configuration file: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Validate required configuration sections
|
||||||
|
if 'mqtt' not in config:
|
||||||
|
logging.error("Missing 'mqtt' section in configuration")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if 'devices' not in config:
|
||||||
|
logging.error("Missing 'devices' section in configuration")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Validate MQTT configuration
|
||||||
|
required_mqtt_fields = ['broker', 'port', 'user', 'password']
|
||||||
|
for field in required_mqtt_fields:
|
||||||
|
if field not in config['mqtt']:
|
||||||
|
logging.error(f"Missing required MQTT field: {field}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Validate device configurations
|
||||||
|
for device_name, device_config in config['devices'].items():
|
||||||
|
required_device_fields = ['ip', 'snmp_community', 'oids']
|
||||||
|
for field in required_device_fields:
|
||||||
|
if field not in device_config:
|
||||||
|
logging.error(f"Missing required field '{field}' in device '{device_name}'")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Validate OID configurations
|
||||||
|
for oid in device_config['oids']:
|
||||||
|
required_oid_fields = ['name', 'oid', 'type', 'HA_device_class', 'HA_platform']
|
||||||
|
for field in required_oid_fields:
|
||||||
|
if field not in oid:
|
||||||
|
logging.error(f"Missing required OID field '{field}' in device '{device_name}'")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Convert type string to actual Python type
|
||||||
|
if oid['type'] == 'int':
|
||||||
|
oid['type'] = int
|
||||||
|
elif oid['type'] == 'bool':
|
||||||
|
oid['type'] = bool
|
||||||
|
elif oid['type'] == 'str':
|
||||||
|
oid['type'] = str
|
||||||
|
else:
|
||||||
|
logging.error(f"Unsupported type '{oid['type']}' for OID '{oid['name']}' in device '{device_name}'")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
logging.info(f"Configuration loaded successfully from {config_path}")
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def connect_mqtt(mqtt_config):
|
||||||
|
def on_connect(client, userdata, flags, rc):
|
||||||
|
if rc == 0:
|
||||||
|
print("Connected to MQTT Broker!")
|
||||||
|
else:
|
||||||
|
print("Failed to connect, return code {rc}")
|
||||||
|
client = mqtt_client.Client()
|
||||||
|
client.username_pw_set(mqtt_config["user"], mqtt_config["password"])
|
||||||
|
client.on_connect = on_connect
|
||||||
|
client.connect(mqtt_config['broker'], mqtt_config['port'])
|
||||||
|
return client
|
||||||
|
|
||||||
|
|
||||||
|
def publish(topic, client, data, retain, qos):
|
||||||
|
msg = json.dumps(data)
|
||||||
|
result = client.publish(topic=topic, payload=msg, qos=qos, retain=bool(retain))
|
||||||
|
status = result[0]
|
||||||
|
if status == 0:
|
||||||
|
logging.debug(f"Send `{msg}` to topic `{topic}`")
|
||||||
|
else:
|
||||||
|
logging.error(f"Failed to send message to topic {topic}")
|
||||||
|
|
||||||
|
|
||||||
|
async def get_snmp(req):
|
||||||
|
data = {}
|
||||||
|
for oid in req["oids"]:
|
||||||
|
with Slim(1) as slim:
|
||||||
|
errorIndication, errorStatus, errorIndex, varBinds = await slim.get(
|
||||||
|
req["snmp_community"],
|
||||||
|
req["ip"],
|
||||||
|
161,
|
||||||
|
ObjectType(ObjectIdentity(oid["oid"])),
|
||||||
|
)
|
||||||
|
|
||||||
|
if errorIndication:
|
||||||
|
logging.error(errorIndication)
|
||||||
|
elif errorStatus:
|
||||||
|
logging.error(
|
||||||
|
"{} at {}".format(
|
||||||
|
errorStatus.prettyPrint(),
|
||||||
|
errorIndex and varBinds[int(errorIndex) - 1][0] or "?",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
for varBind in varBinds:
|
||||||
|
logging.debug(f"{req['device_name']} {oid['name']} => {oid['type'](varBind[1])}")
|
||||||
|
if oid['type'] == bool:
|
||||||
|
if bool(varBind[1]):
|
||||||
|
data.update({oid["name"]: "ON"})
|
||||||
|
else:
|
||||||
|
data.update({oid["name"]: "OFF"})
|
||||||
|
else:
|
||||||
|
data.update({oid["name"]: oid["type"](varBind[1])})
|
||||||
|
logging.debug(f"JSON : {json.dumps(data)}")
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def ha_create_config(req):
|
||||||
|
ha_config = {}
|
||||||
|
device = {
|
||||||
|
"ids": f"{req['device_name']}_{req['ip']}".replace(".", "_"),
|
||||||
|
"name": req['device_name'],
|
||||||
|
}
|
||||||
|
origin = {
|
||||||
|
"name": "snmp2mqtt"
|
||||||
|
}
|
||||||
|
ha_config.update({"dev": device, "o": origin})
|
||||||
|
ha_config.update({"state_topic": f"SNMP/{req['device_name']}/state"})
|
||||||
|
ha_config.update({"qos": 2})
|
||||||
|
cmps = {}
|
||||||
|
for oid in req['oids']:
|
||||||
|
cmps.update(
|
||||||
|
{
|
||||||
|
f"{req['device_name']}_{req['ip']}_{oid['name']}".replace(".", "_"):
|
||||||
|
{
|
||||||
|
"p": oid['HA_platform'],
|
||||||
|
"device_class": oid['HA_device_class'],
|
||||||
|
"value_template": f"{{{{ value_json.{oid['name']}}}}}",
|
||||||
|
"unique_id": f"{req['device_name']}_{req['ip']}_{oid['name']}".replace(".", "_"),
|
||||||
|
"name": oid['name']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if "HA_unit" in oid.keys():
|
||||||
|
cmps.update(
|
||||||
|
{f"{req['device_name']}_{req['ip']}_{oid['name']}".replace(".", "_"):
|
||||||
|
{"unit_of_measurement": oid['HA_unit']}})
|
||||||
|
ha_config.update({"cmps": cmps})
|
||||||
|
logging.debug(f"config : {json.dumps(ha_config)}")
|
||||||
|
return ha_config
|
||||||
|
|
||||||
|
|
||||||
|
def send_to_mqtt(device_name, device_config, mqtt_config, sleep_interval=2):
|
||||||
|
"""Send SNMP data to MQTT for a single device"""
|
||||||
|
# Create device request object
|
||||||
|
req = {
|
||||||
|
"device_name": device_name,
|
||||||
|
"ip": device_config["ip"],
|
||||||
|
"snmp_community": device_config["snmp_community"],
|
||||||
|
"oids": device_config["oids"]
|
||||||
|
}
|
||||||
|
|
||||||
|
config = ha_create_config(req)
|
||||||
|
client = connect_mqtt(mqtt_config)
|
||||||
|
client.loop_start()
|
||||||
|
config_topic = f"homeassistant/device/{config['dev']['ids']}/config"
|
||||||
|
state_topic = config['state_topic']
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
publish(config_topic, client, config, True, 0)
|
||||||
|
logging.info(f"{config_topic} -> {config}")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error publishing config for {device_name}: {e}")
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
state = asyncio.run(get_snmp(req))
|
||||||
|
publish(state_topic, client, state, False, 0)
|
||||||
|
logging.info(f"{state_topic} -> {state}")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error getting SNMP data for {device_name}: {e}")
|
||||||
|
pass
|
||||||
|
sleep(sleep_interval)
|
||||||
|
|
||||||
|
|
||||||
|
def process_devices(config):
|
||||||
|
"""Process multiple devices from configuration"""
|
||||||
|
mqtt_config = config['mqtt'].copy()
|
||||||
|
mqtt_config['client_id'] = f"snmp-mqtt-{random.randint(0, 1000)}"
|
||||||
|
|
||||||
|
# Get sleep interval from config or use default
|
||||||
|
sleep_interval = config.get('sleep_interval', 2)
|
||||||
|
|
||||||
|
if len(config['devices']) == 1:
|
||||||
|
# Single device mode - run directly
|
||||||
|
device_name = list(config['devices'].keys())[0]
|
||||||
|
device_config = config['devices'][device_name]
|
||||||
|
logging.info(f"Starting monitoring for single device: {device_name}")
|
||||||
|
send_to_mqtt(device_name, device_config, mqtt_config, sleep_interval)
|
||||||
|
else:
|
||||||
|
# Multiple devices mode - would need threading/multiprocessing
|
||||||
|
# For now, let's process the first device and warn about others
|
||||||
|
logging.warning(f"Multiple devices detected ({len(config['devices'])}), but only processing the first one")
|
||||||
|
logging.warning("Multi-device support will require threading implementation")
|
||||||
|
|
||||||
|
device_name = list(config['devices'].keys())[0]
|
||||||
|
device_config = config['devices'][device_name]
|
||||||
|
logging.info(f"Starting monitoring for device: {device_name}")
|
||||||
|
send_to_mqtt(device_name, device_config, mqtt_config, sleep_interval)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main entry point"""
|
||||||
|
args = parse_arguments()
|
||||||
|
config = load_config(args.config)
|
||||||
|
|
||||||
|
logging.info("Starting snmp2mqtt bridge...")
|
||||||
|
logging.info(f"Configured devices: {list(config['devices'].keys())}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
process_devices(config)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
logging.info("Shutdown requested by user")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Unexpected error: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Reference in New Issue
Block a user