mirror of
https://github.com/librenms/librenms-agent.git
synced 2024-05-09 09:54:52 +00:00
Raspberry Pi: Add SNMP extend to monitor IO pins or sensor modules connected to the GPIO header (#364)
* Raspberry Pi: Add SNMP extend to monitor IO pins or sensor modules connected to the GPIO header * Raspberry Pi: Add missing sensor types
This commit is contained in:
92
snmp/rpigpiomonitor.ini
Normal file
92
snmp/rpigpiomonitor.ini
Normal file
@@ -0,0 +1,92 @@
|
||||
; Note
|
||||
;
|
||||
; If a configured sensor does not appear in librenms, this may be due to a faulty configuration.
|
||||
; After the configuration has been changed, it can be checked for possible errors using "rpigpiomonitor.php -validate".
|
||||
;
|
||||
; Any change to your configuration requires a manual rediscover of your device. Otherwise, your changes will only be visible once librenms performs an automatic rediscover.
|
||||
|
||||
; Sensor section
|
||||
;
|
||||
; Multiple use of the identical name can lead to overwriting of the state configuration of the first sensor with the same name when using states.
|
||||
; This should therefore only be used once per configuration if possible. When using multiple Raspberry's, this can be used once per device because the serial number of the Raspberry is added to each sensor.
|
||||
[sensorexample1]
|
||||
|
||||
; Sensor type
|
||||
;
|
||||
; This defines the sensor type.
|
||||
; When reading a normal IO contact, the use of "state" is recommended.
|
||||
; The following types are possible:
|
||||
; airflow, ber, charge, chromatic_dispersion, cooling, count, current, dbm, delay, eer, fanspeed
|
||||
; frequency, humidity, load, loss, power, power_consumed, power_factor, pressure, quality_factor
|
||||
; runtime, signal, snr, state, temperature, tv_signal, voltage, waterflow, percent
|
||||
type = state
|
||||
|
||||
; Sensor description
|
||||
;
|
||||
; This defines the sensor description which can be seen later in the respective device overview or in the graph.
|
||||
description = Cabinet door
|
||||
|
||||
; Sensor limits
|
||||
;
|
||||
; This defines the limits from when the respective alarm should be triggered.
|
||||
; Only numeric values are valid!
|
||||
; If a certain alarm or even all of them should not be used, these values can be omitted or commented out.
|
||||
;lowlimit = 0
|
||||
;lowwarnlimit = 5
|
||||
;warnlimit = 30
|
||||
;highlimit = 35
|
||||
|
||||
; Sensor states
|
||||
;
|
||||
; This defines the sensor states to be used. If no sensor state is desired it can be omitted or commented out.
|
||||
; A state sensor is configured in the following format:
|
||||
; states.state_description.state_variable = state_value
|
||||
; state_description should be replaced with the desired sensor state description which is displayed in librenms.
|
||||
; state_variable should be replaced with the variable to be configured. The 2 variables "value" and "generic" must be configured for each state.
|
||||
; The variable "value" specifies the value when the respective state is to be displayed.
|
||||
; The variable "generic" can be used to define the background color of the respective state. 0 = green, 1 = orange, 2 = red.
|
||||
; In the following example, the "Cabinet door" sensor will display "Open" in red for value 1 and "Closed" in green for value 0:
|
||||
states.Open.value = 1
|
||||
states.Open.generic = 2
|
||||
states.Closed.value = 0
|
||||
states.Closed.generic = 0
|
||||
|
||||
; GPIO readout of an IO contact
|
||||
;
|
||||
; This defines the respective GPIO PIN which is to be read out.
|
||||
; With a pullup or pulldown circuit the state of a door contact can be determined.
|
||||
; It is important that the pin numbering scheme of WiringPi is used!
|
||||
io_gpio_pin = 21
|
||||
|
||||
; GPIO readout by external program
|
||||
;
|
||||
; This defines the external program to be used to read out the respective GPIO values.
|
||||
; Hereby it is possible to read sensor modules like temperature sensors, air pressure sensors, humidity sensors etc..
|
||||
; The program to be used must output a pure numerical value. If this is not the case, the value can be extracted from the output of the program using a helper script, for example.
|
||||
;external_gpio_reader = /etc/snmp/tempreader.sh
|
||||
|
||||
|
||||
|
||||
;
|
||||
; A few more example configurations
|
||||
;
|
||||
|
||||
; Example configuration which reads an IO contact (pin 25) to determine whether the light of a technical cabinet is switched on or off.
|
||||
[sensorexample2]
|
||||
type = state
|
||||
description = Cabinet lighting
|
||||
states.Switched on.value = 1
|
||||
states.Switched on.generic = 2
|
||||
states.Switched off.value = 0
|
||||
states.Switched off.generic = 0
|
||||
io_gpio_pin = 25
|
||||
|
||||
; Example configuration which reads a temperature sensor
|
||||
;[sensorexample3]
|
||||
;type = temperature
|
||||
;description = Cabinet temperature
|
||||
;lowlimit = 0
|
||||
;lowwarnlimit = 5
|
||||
;warnlimit = 35
|
||||
;highlimit = 40
|
||||
;external_gpio_reader = /etc/snmp/tempreader.sh
|
290
snmp/rpigpiomonitor.php
Executable file
290
snmp/rpigpiomonitor.php
Executable file
@@ -0,0 +1,290 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* rpigpiomonitor.php
|
||||
*
|
||||
* LibreNMS Raspberry Pi GPIO Monitor SNMP extension
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @link https://librenms.org
|
||||
* @copyright 2021 Denny Friebe
|
||||
* @author Denny Friebe <denny.friebe@icera-network.de>
|
||||
*/
|
||||
|
||||
function parseConfigFile($file, $process_sections = false, $scanner_mode = INI_SCANNER_NORMAL) {
|
||||
$explode_str = '.';
|
||||
$escape_char = "'";
|
||||
|
||||
// load ini file the normal way
|
||||
$data = parse_ini_file($file, $process_sections, $scanner_mode);
|
||||
|
||||
if (!$process_sections) {
|
||||
$data = array($data);
|
||||
}
|
||||
|
||||
foreach ($data as $section_key => $section) {
|
||||
// loop inside the section
|
||||
foreach ($section as $key => $value) {
|
||||
if (strpos($key, $explode_str)) {
|
||||
if (substr($key, 0, 1) !== $escape_char) {
|
||||
// key has a dot. Explode on it, then parse each subkeys
|
||||
// and set value at the right place thanks to references
|
||||
$sub_keys = explode($explode_str, $key);
|
||||
$subs =& $data[$section_key];
|
||||
foreach ($sub_keys as $sub_key) {
|
||||
if (!isset($subs[$sub_key])) {
|
||||
$subs[$sub_key] = [];
|
||||
}
|
||||
$subs =& $subs[$sub_key];
|
||||
}
|
||||
// set the value at the right place
|
||||
$subs = $value;
|
||||
// unset the dotted key, we don't need it anymore
|
||||
unset($data[$section_key][$key]);
|
||||
}
|
||||
// we have escaped the key, so we keep dots as they are
|
||||
else {
|
||||
$new_key = trim($key, $escape_char);
|
||||
$data[$section_key][$new_key] = $value;
|
||||
unset($data[$section_key][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$process_sections) {
|
||||
$data = $data[0];
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
function validate_sensor_type($type) {
|
||||
switch ($type) {
|
||||
case "airflow":
|
||||
case "ber":
|
||||
case "charge":
|
||||
case "chromatic_dispersion":
|
||||
case "cooling":
|
||||
case "count":
|
||||
case "current":
|
||||
case "dbm":
|
||||
case "delay":
|
||||
case "eer":
|
||||
case "fanspeed":
|
||||
case "frequency":
|
||||
case "humidity":
|
||||
case "load":
|
||||
case "loss":
|
||||
case "power":
|
||||
case "power_consumed":
|
||||
case "power_factor":
|
||||
case "pressure":
|
||||
case "quality_factor":
|
||||
case "runtime":
|
||||
case "signal":
|
||||
case "snr":
|
||||
case "state":
|
||||
case "temperature":
|
||||
case "tv_signal":
|
||||
case "voltage":
|
||||
case "waterflow":
|
||||
case "percent":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function validate_sensor_states($states) {
|
||||
if (is_array($states)) {
|
||||
foreach($states as $state_index => $state) {
|
||||
if (!isset($state["value"]) || !isset($state["generic"])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_numeric($state["value"]) || !is_numeric($state["generic"])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function validate_sensor_limit($limit) {
|
||||
if (isset($limit) && is_numeric($limit)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function get_rpi_serial() {
|
||||
if (file_exists("/proc/device-tree/serial-number")) {
|
||||
$rpi_serial = file_get_contents("/proc/device-tree/serial-number");
|
||||
//During the readout of serial-number additional characters are passed. (at this point I am not sure why)
|
||||
//To prevent these characters from being output and messing up the whole snmp string we only cut out the needed characters.
|
||||
$rpi_serial = substr($rpi_serial, 0, 16);
|
||||
return $rpi_serial;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
function get_sensor_current_value($sensor_data) {
|
||||
if (isset($sensor_data["io_gpio_pin"])) {
|
||||
$sensor_current_value = exec("gpio read " .$sensor_data["io_gpio_pin"]. " 2>&1", $tt, $retcode);
|
||||
} else {
|
||||
$sensor_current_value = exec($sensor_data["external_gpio_reader"]. " 2>&1", $tt, $retcode);
|
||||
}
|
||||
|
||||
if (is_numeric($sensor_current_value)) {
|
||||
return $sensor_current_value;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function validate_config($config, $rpi_serial) {
|
||||
if(!$rpi_serial) {
|
||||
echo "The serial number of your raspberry pi could not be read. Please check if you are using a DT enabled kernel and the file /proc/device-tree/serial-number is present. \n";
|
||||
echo "The serial number is required for creating a state sensor so that no sensor with the same name from another RPI overwrites it. \n";
|
||||
}
|
||||
|
||||
foreach($config as $sensor_name => $sensor_data) {
|
||||
$valid = false;
|
||||
$gpio_reader_valid = true;
|
||||
|
||||
if (!isset($sensor_data["type"]) || validate_sensor_type($sensor_data["type"]) == false) {
|
||||
echo "No valid type is configured for sensor ".$sensor_name."! \n";
|
||||
}
|
||||
|
||||
if (isset($sensor_data["states"]) && validate_sensor_states($sensor_data["states"]) == false) {
|
||||
echo "No valid states is configured for sensor ".$sensor_name."! \n";
|
||||
}
|
||||
|
||||
if (!$sensor_data["description"]) {
|
||||
echo "No valid description is configured for sensor ".$sensor_name."! \n";
|
||||
}
|
||||
|
||||
if (isset($sensor_data["lowlimit"]) && validate_sensor_limit($sensor_data["lowlimit"]) == false) {
|
||||
echo "No valid lowlimit is configured for sensor ".$sensor_name."! \n";
|
||||
}
|
||||
|
||||
if (isset($sensor_data["lowwarnlimit"]) && validate_sensor_limit($sensor_data["lowwarnlimit"]) == false) {
|
||||
echo "No valid lowwarnlimit is configured for sensor ".$sensor_name."! \n";
|
||||
}
|
||||
|
||||
if (isset($sensor_data["warnlimit"]) && validate_sensor_limit($sensor_data["warnlimit"]) == false) {
|
||||
echo "No valid warnlimit is configured for sensor ".$sensor_name."! \n";
|
||||
}
|
||||
|
||||
if (isset($sensor_data["highlimit"]) && validate_sensor_limit($sensor_data["highlimit"]) == false) {
|
||||
echo "No valid highlimit is configured for sensor ".$sensor_name."! \n";
|
||||
}
|
||||
|
||||
if (!isset($sensor_data["io_gpio_pin"]) && !isset($sensor_data["external_gpio_reader"])) {
|
||||
echo "No IO GPIO pin or external GPIO readout program is configured for sensor ".$sensor_name."! \n";
|
||||
$gpio_reader_valid = false;
|
||||
}
|
||||
|
||||
if (isset($sensor_data["external_gpio_reader"]) && !file_exists($sensor_data["external_gpio_reader"])) {
|
||||
echo "The external GPIO program for sensor ".$sensor_name." could not be found! Please check if the specified path is correct and the file exists. \n";
|
||||
$gpio_reader_valid = false;
|
||||
}
|
||||
|
||||
if ($gpio_reader_valid) {
|
||||
$sensor_current_value = get_sensor_current_value($sensor_data);
|
||||
if (isset($sensor_current_value)) {
|
||||
echo "Current sensor value for ".$sensor_name.": " . $sensor_current_value . "\n";
|
||||
$valid = true;
|
||||
} else {
|
||||
echo "The current sensor value for ".$sensor_name." does not seem to be numeric! \n";
|
||||
if (isset($sensor_data["io_gpio_pin"])) {
|
||||
echo "Please check if wiringpi is installed on this device! \n";
|
||||
} else {
|
||||
echo "Please check if the external GPIO program outputs pure numeric values and if the required access rights are available to execute this program. \n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($valid) {
|
||||
echo "The sensor ".$sensor_name." are configured correctly. \n\n";
|
||||
} else {
|
||||
echo "Please check your configuration for sensor ".$sensor_name.". \n\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function read_sensors($config, $rpi_serial) {
|
||||
if ($rpi_serial) {
|
||||
foreach($config as $sensor_name => $sensor_data) {
|
||||
if ((!isset($sensor_data["type"]) || validate_sensor_type($sensor_data["type"]) == false)
|
||||
|| (isset($sensor_data["states"]) && validate_sensor_states($sensor_data["states"]) == false)
|
||||
|| !$sensor_data["description"]
|
||||
|| (isset($sensor_data["lowlimit"]) && validate_sensor_limit($sensor_data["lowlimit"]) == false)
|
||||
|| (isset($sensor_data["lowwarnlimit"]) && validate_sensor_limit($sensor_data["lowwarnlimit"]) == false)
|
||||
|| (isset($sensor_data["warnlimit"]) && validate_sensor_limit($sensor_data["warnlimit"]) == false)
|
||||
|| (isset($sensor_data["highlimit"]) && validate_sensor_limit($sensor_data["highlimit"]) == false)
|
||||
|| (!isset($sensor_data["io_gpio_pin"]) && !isset($sensor_data["external_gpio_reader"]))
|
||||
|| (isset($sensor_data["external_gpio_reader"]) && !file_exists($sensor_data["external_gpio_reader"]))) {
|
||||
continue; //The configuration of this sensor is not correct. Skip this one.
|
||||
}
|
||||
|
||||
$sensor_current_value = get_sensor_current_value($sensor_data);
|
||||
if (!isset($sensor_current_value)) {
|
||||
continue; //The value read from the sensor does not correspond to a numerical value. Skip this one.
|
||||
}
|
||||
|
||||
//If limit is not configured, we initialize the respective key to prevent "Undefined index" notes.
|
||||
if (!isset($sensor_data["lowlimit"])) {
|
||||
$sensor_data["lowlimit"] = null;
|
||||
}
|
||||
|
||||
if (!isset($sensor_data["lowwarnlimit"])) {
|
||||
$sensor_data["lowwarnlimit"] = null;
|
||||
}
|
||||
|
||||
if (!isset($sensor_data["warnlimit"])) {
|
||||
$sensor_data["warnlimit"] = null;
|
||||
}
|
||||
|
||||
if (!isset($sensor_data["highlimit"])) {
|
||||
$sensor_data["highlimit"] = null;
|
||||
}
|
||||
|
||||
echo $sensor_name."_".$rpi_serial.",".$sensor_data["type"].",".$sensor_data["description"].",".$sensor_data["lowlimit"].",".$sensor_data["lowwarnlimit"].",".$sensor_data["warnlimit"].",".$sensor_data["highlimit"]. ";";
|
||||
|
||||
if(isset($sensor_data["states"])) {
|
||||
foreach($sensor_data["states"] as $state_descr => $state) {
|
||||
echo $state["value"].",".$state["generic"].",".$state_descr.";";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n" . $sensor_current_value . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$config = parseConfigFile('rpigpiomonitor.ini', true);
|
||||
$rpi_serial = get_rpi_serial();
|
||||
|
||||
for ($i=0; $i < $argc; $i++) {
|
||||
if ($argv[$i] == "-validate") {
|
||||
validate_config($config, $rpi_serial);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
read_sensors($config, $rpi_serial);
|
||||
?>
|
||||
|
Reference in New Issue
Block a user