From 379482e72969f01c9a308a7af5c6592071c4368f Mon Sep 17 00:00:00 2001 From: Frederik Lindenaar <frederik@lindenaar.nl> Date: Sun, 10 Mar 2019 13:01:49 +0100 Subject: [PATCH] Added Bosch Sensortec I2C BME280/BMP280 sensor Added support for additional measurements in addition to the temperature Fixed handling of default I2C address Updated documentation for the new I2C sensors (and fixed a few errors) --- README.md | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- plugins/check_temperature | 262 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------- 2 files changed, 303 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index c294809..aa3794b 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,8 @@ This repository contains the following scripts: * [check_otp](#check_otp) plugin to monitor PrivacyIDEA (and LinOTP) OTP validation * [check_temperature](#check_temperature) - plugin to monitor the CPU temperature of a RaspberryPi or that of a DS18b20 1-wire sensor on a RaspberryPi + plugin to monitor the RaspberryPi CPU temperature or that of an 1-wire + (DS18b20) or I2C (MCP9080) sensor attached to a RaspberryPi * [nagiosstatus](#nagiosstatus) CGI-BIN script to report the status of nagios (to monitor nagios itself) @@ -239,68 +240,121 @@ define service { ------------------------------------------------------- Plugin (check) to monitor monitor the Raspberry Pi CPU temperature or that of a temperature sensor connected to a RaspberryPi. This implementation currently -supports the DS18B20 1-wire temperature sensor. Other methods and interfaces can -be plugged in easily (just raise a request or provide a patch). For information -on how to connect sensor to the RaspberryPi and to get it working please see -[this Adafruit tutorial]( -https://learn.adafruit.com/adafruits-raspberry-pi-lesson-11-ds18b20-temperature-sensing). - -No setup is required to read the CPU temperature. To enable the 1-wire interface -support on the RaspberryPi one can use the command: -~~~ - sudo raspi-config nonint do_onewire 0 -~~~ -or use `raspi-config` in interactive mode (9. Advanced Options --> A9. 1-Wire). -Please note that changing this requires a reboot. - -Installation for is straightforward, after installing the script on the server -add the following to your Nagios `commands.cmd` configuration file: - +supports the following sensors: + * 1-wire: + * Maxim Integrated [DS18B20](https://learn.adafruit.com/adafruits-raspberry-pi-lesson-11-ds18b20-temperature-sensing) + * I2C + * Bosch Sensortec [BME280](https://learn.adafruit.com/adafruit-bme280-humidity-barometric-pressure-temperature-sensor-breakout) + * Bosch Sensortec [BMP280](https://learn.adafruit.com/adafruit-bmp280-barometric-pressure-plus-temperature-sensor-breakout) + * Microchip [MCP9080](https://learn.adafruit.com/adafruit-mcp9808-precision-i2c-temperature-sensor-guide) + +Other methods and interfaces can be plugged in easily (just raise a request or +provide a patch). For information on how to connect sensor to the RaspberryPi +and to get it working please click on the links in the list above. As per these, +most sensors require some configuration to make them available: + * No setup is required to read the CPU temperature. + * To enable 1-wire interface support on the RaspberryPi use the command: + ~~~ + sudo raspi-config nonint do_onewire 0 + ~~~ + or use `raspi-config` interactively (1. Interfacing Options --> P7. 1-Wire). + Please note that changing this requires a reboot. + * To enable I2C interface support on the RaspberryPi use the command: + ~~~ + sudo raspi-config nonint do_i2c 0 + ~~~ + or use `raspi-config` interactively (1. Interfacing Options --> P5. I2C). + Please note that changing this requires a reboot. + The I2C interface also requires the `SMBus` or `SMBus2` library, to install + the `SMBus` library on Raspbian Linux run: + ~~~ + sudo apt install python-smbus + ~~~ + `SMBus2` is a pure Python implementation that requires system-wide or a + `virtualenv`-based installation, less trivial than installing the package. + +Configuration of Nagios to use the script is straightforward, after installing +the script on the server add the following to your Nagios `commands.cmd` +configuration file to enable checking the CPU temperature: ~~~ # 'check_cpu_temperature' command definition to monitor CPU temperature in C # parameters: warning (ARG1) and critical (ARG2) temperature in Celcius define command { - command_name check_temperature + command_name check_cpu_temperature command_line [install_path]/plugins/check_temperature -w $ARG1$ -c $ARG2$ rpi_cpu } # 'check_cpu_ftemperature' command definition to monitor CPU temperature in F # parameters: warning (ARG1) and critical (ARG2) temperature in Celcius define command { - command_name check_temperature + command_name check_cpu_ftemperature command_line [install_path]/plugins/check_temperature -F -w $ARG1$ -c $ARG2$ rpi_cpu } +~~~ +To monitor a supported temperature sensor on its default address, add: +~~~ # 'check_temperature' command definition to monitor a single temperature in C # parameters: warning (ARG1) and critical (ARG2) temperature in Celcius define command { - command_name check_temperature - command_line [install_path]/plugins/check_temperature -w $ARG1$ -c $ARG2$ w1_ds18b20 + command_name check_cpu_temperature + command_line [install_path]/plugins/check_temperature -w $ARG1$ -c $ARG2$ <<sensor>> } # 'check_ftemperature' command definition to monitor a single temperature in F # parameters: warning (ARG1) and critical (ARG2) temperature in Farenheit define command { - command_name check_ftemperature - command_line [install_path]/plugins/check_temperature -F -w $ARG1$ -c $ARG2$ w1_ds18b20 + command_name check_cpu_temperature_f + command_line [install_path]/plugins/check_temperature -F -w $ARG1$ -c $ARG2$ <<sensor>> } +~~~ +With `<<sensor>>` replaced by the sensor, e.g. w1_ds18b20 for a 1-wire DS18B20, +i2c_mcp9808 for an I2C MCP9808 sensor or i2c_bme280 for an I2C BME280. Run the + +In case you have multiple sensors, add multiple definitions with different +values for `command_name`. +If you need to pass on additional parameters, e.g. the sensor serial for an +1-wire DS18B20, you can do that like this: +~~~ # 'check_temperature_sensor' command definition to monitor a single temperature in C # parameters: sensor serial (ARG1), warning (ARG2) and critical (ARG3) temperature in Celcius define command { - command_name check_temperature_sensor + command_name check_ds18b20_sensor command_line [install_path]/plugins/check_temperature -w $ARG2$ -c $ARG3$ w1_ds18b20 -s $ARG1$ } # 'check_ftemperature_sensor' command definition to monitor a single temperature in F # parameters: sensor serial (ARG1), warning (ARG2) and critical (ARG3) temperature in Farenheit define command { - command_name check_ftemperature_sensor + command_name check_ds18b20_sensor_f command_line [install_path]/plugins/check_temperature -F -w $ARG2$ -c $ARG3$ w1_ds18b20 -s $ARG1$ } ~~~ +Likewise, to pass the I2C address for an I2C MCP9808 use something like: +~~~ +# 'check_temperature_sensor' command definition to monitor a single temperature in C +# parameters: sensor address (ARG1), warning (ARG2) and critical (ARG3) temperature in Celcius +define command { + command_name check_mcp9808_sensor + command_line [install_path]/plugins/check_temperature -w $ARG2$ -c $ARG3$ i2c_mcp9808 -a $ARG1$ +} + +# 'check_ftemperature_sensor' command definition to monitor a single temperature in F +# parameters: sensor address (ARG1), warning (ARG2) and critical (ARG3) temperature in Farenheit +define command { + command_name check_mcp9808_sensor_f + command_line [install_path]/plugins/check_temperature -F -w $ARG2$ -c $ARG3$ i2c_mcp9808 -a $ARG1$ +} + +~~~ + +For the list of available sensors, please run `check_temperature -h` and run +`check_temperature <<sensor>> -h` to get the list of accepted options for sensor +``<<sensor>>``. + Make sure to replace `[install_path]/plugins` with the location of the script. To use the it define a service check like below: @@ -313,7 +367,7 @@ define service { use generic-service } -# check temperature in Celcius using a DS18B20 sensor connected to a RaspberryPi +# check temperature in Celcius using a sensor connected to a RaspberryPi define service { host hostname.mydomain.tld service_description Check Temperature @@ -325,7 +379,15 @@ define service { define service { host hostname.mydomain.tld service_description Check Temperature - check_command check_temperature_sensor!0000a31ea3de!30!35 + check_command check_ds18b20_sensor!0000a31ea3de!30!35 + use generic-service +} + +# check temperature with MCP9808 sensor 0x19 connected to a RaspberryPi +define service { + host hostname.mydomain.tld + service_description Check Temperature + check_command check_mcp9808_sensor!0x19!30!35 use generic-service } ~~~ diff --git a/plugins/check_temperature b/plugins/check_temperature index 3a96d06..f9a79e9 100755 --- a/plugins/check_temperature +++ b/plugins/check_temperature @@ -33,6 +33,25 @@ PROG_VERSION=PROG_NAME + ' ' + VERSION CPU_SENSOR_DEV = '/sys/class/thermal/thermal_zone0/temp' CPU_SENSOR_SCALE=1000 + +I2C_BMX280_DEFAULT_ADDR=0x77 +I2C_BMX280_CALIBRATE_ADDR=0x88 +I2C_BMX280_CALIBRATE_LEN=24 +I2C_BMX280_CAL_HUM0_ADDR=0xA1 +I2C_BMX280_CAL_HUM_ADDR=0xE1 +I2C_BMX280_CAL_HUM_LEN=7 +I2C_BMX280_CONFIG_ADDR=0xF5 +I2C_BMX280_CONFIG = 0xA0 # Stand_by time = 1000 ms +I2C_BMX280_CTRL_MEAS_ADDR=0xF4 +I2C_BMX280_CTRL_MEAS=0x27 # Normal mode, Pressure + # and Temperature Oversampling rate = 1 +I2C_BMX280_CTRL_HUM_ADDR=0xF2 +I2C_BMX280_CTRL_HUM=1 # Humidity Oversampling rate = 1 +I2C_BMX280_MEAS_ADDR=0xF7 +I2C_BMX280_MEAS_LEN=8 +I2C_BMX280_SENSOR_SCALE=5120.0 + +I2C_MCP9808_DEFAULT_ADDR=0x18 I2C_MCP9808_CONFIG_ADDR=0x1 I2C_MCP9808_CONFIG = [ 0x00, 0x00 ] # continuous conversion (power-up default) I2C_MCP9808_PRECISION_ADDR=0x08 @@ -89,6 +108,11 @@ class SetLogFile(StoreAction): ############################################################################### +def hex_int(string): + """Use int()'s auto-detection to parse 10-base and 16-base (0x..) numbers""" + return int(string, 0); + + def convert_celcius(temp_read, scale = 1): """Converts raw temperature sensore value to degrees Celcius""" return float(temp_read) / float(scale) @@ -101,18 +125,9 @@ def convert_farenheit(temp_read, scale = 1): CONVERT_FARENHEIT = ( convert_farenheit, 'F', 'Farenheit' ) -def isempty(string): - """Checks whether string 'str' provided is unset or empty""" - return string is None or len(string) == 0 - - -def hex_int(string): - """Use int()'s auto-detection to parse 10-base and 16-base (0x..) numbers""" - return int(string, 0); - - +####################[ Get CPU temperature of Raspberry Pi ]#################### def read_rpi_cpu_temp(args): - """Reads CPU temperature and converts it to desired unit, returns temperature""" + """Reads CPU temperature ands returns it converted to desired unit""" with open(args.file, 'r') as f: lines = f.readlines() logger.debug('Temperature sensor data read from %s: %s', f.name, lines) @@ -124,9 +139,8 @@ def read_rpi_cpu_temp(args): return temp, 1 -def read_i2c_mcp9808_temp(args): - """Returns temperature from I2C MCP9808 sensor in desired unit""" - +###############[ Get I2C (sm)bus object and I2C device address ]############### +def i2c_get_smbus_devaddr(args): try: import smbus except ImportError: @@ -134,31 +148,149 @@ def read_i2c_mcp9808_temp(args): import smbus2 as smbus except ImportError: logger.critical("Unable to import either smbus or smbus2 library"); - raise ImportError("missing I2C library, please install python-smbus or smbus2"); - + raise ImportError("missing I2C library, please install smbus2 " + "or Debian python-smbus package "); try: - bus = smbus.SMBus(args.i2cbus) # Get I2C bus + return (smbus.SMBus(args.i2cbus), # get i2c bus + args.default_address if args.address is None else args.address) except OSError as e: logger.critical(e) raise IOError("Invalid I2C bus: %d" % args.i2cbus) +####################[ Get I2C BME280/BMP280 Sensor values ]#################### +# Inspired by https://github.com/ControlEverythingCommunity/BME280 +def read_i2c_bmX280(args): + """Returns temperature from I2C BME280/BMP280 sensor in desired unit""" + + i2c_bus, i2c_addr = i2c_get_smbus_devaddr(args) + + def convertLE16(data, offset, signed=False): + result = (data[offset + 1] << 8) | data[offset] + if signed and result > 32767: + result -= 65536 + return result + try: - bus.write_i2c_block_data(args.address, I2C_MCP9808_CONFIG_ADDR, + # Get Temperature and Pressure Calibration Data + data = i2c_bus.read_i2c_block_data(i2c_addr, I2C_BMX280_CALIBRATE_ADDR, + I2C_BMX280_CALIBRATE_LEN) + dig_T1 = convertLE16(data, 0) + dig_T2 = convertLE16(data, 2, True) + dig_T3 = convertLE16(data, 4, True) + dig_P1 = convertLE16(data, 6) + dig_P2 = convertLE16(data, 8, True) + dig_P3 = convertLE16(data, 10, True) + dig_P4 = convertLE16(data, 12, True) + dig_P5 = convertLE16(data, 14, True) + dig_P6 = convertLE16(data, 16, True) + dig_P7 = convertLE16(data, 18, True) + dig_P8 = convertLE16(data, 20, True) + dig_P9 = convertLE16(data, 22, True) + + if args.read_humidity: + # Get Humidity Calibration Data + dig_H1 = i2c_bus.read_byte_data(i2c_addr,I2C_BMX280_CAL_HUM0_ADDR) + data = i2c_bus.read_i2c_block_data(i2c_addr,I2C_BMX280_CAL_HUM_ADDR, + I2C_BMX280_CAL_HUM_LEN) + dig_H2 = convertLE16(data, 0, True) + dig_H3 = data[2] + dig_H4 = (data[3] << 4) | (data[4] & 0x0f) + dig_H5 = (data[5] << 4) | (data[4] >> 4) + dig_H6 = data[6] + if dig_H6 > 127: + dig_H6 -= 256 + + i2c_bus.write_byte_data(i2c_addr, I2C_BMX280_CTRL_HUM_ADDR, + I2C_BMX280_CTRL_HUM) + + # Setup BMP280/BME280 configuration and wait 0.5 seconds for things to settle + i2c_bus.write_byte_data(i2c_addr, I2C_BMX280_CTRL_MEAS_ADDR, + I2C_BMX280_CTRL_MEAS) + i2c_bus.write_byte_data(i2c_addr, I2C_BMX280_CONFIG_ADDR, + I2C_BMX280_CONFIG) + sleep(0.5) + + # Read sensor data and and convert using calibration data + data = i2c_bus.read_i2c_block_data(i2c_addr, I2C_BMX280_MEAS_ADDR, + I2C_BMX280_MEAS_LEN) + + # Convert 20-bits Temperature and calculate value with calibration data + adc_t = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4) + var1 = ((adc_t) / 16384.0 - (dig_T1) / 1024.0) * (dig_T2) + var2 = (((adc_t) / 131072.0 - (dig_T1) / 8192.0) * ((adc_t)/131072.0 - (dig_T1)/8192.0)) * (dig_T3) + t_fine = (var1 + var2) + + # Convert 20-bits Pressure and calculate value with calibration data + adc_p = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4) + var1 = (t_fine / 2.0) - 64000.0 + var2 = var1 * var1 * (dig_P6) / 32768.0 + var2 = var2 + var1 * (dig_P5) * 2.0 + var2 = (var2 / 4.0) + ((dig_P4) * 65536.0) + var1 = ((dig_P3) * var1 * var1 / 524288.0 + ( dig_P2) * var1) / 524288.0 + var1 = (1.0 + var1 / 32768.0) * (dig_P1) + p = 1048576.0 - adc_p + p = (p - (var2 / 4096.0)) * 6250.0 / var1 + var1 = (dig_P9) * p * p / 2147483648.0 + var2 = p * (dig_P8) / 32768.0 + pressure = (p + (var1 + var2 + (dig_P7)) / 16.0) / 100 + + extra_readings = [ ('pressure', pressure, 'hPa'), ] + + if args.read_humidity: + # Convert 16-byte Humidity and calculate value with calibration data + adc_h = (data[6] << 8) | data[7] + var_H = ((t_fine) - 76800.0) + var_H = (adc_h - (dig_H4 * 64.0 + dig_H5 / 16384.0 * var_H)) * (dig_H2 / 65536.0 * (1.0 + dig_H6 / 67108864.0 * var_H * (1.0 + dig_H3 / 67108864.0 * var_H))) + humidity = var_H * (1.0 - dig_H1 * var_H / 524288.0) + if humidity > 100.0 : + humidity = 100.0 + elif humidity < 0.0 : + humidity = 0.0 + extra_readings += ('humidity', humidity, '%', 0, 100), + logger.debug('BME280 sensor data: temperature %#02x: %#x = %d, ' + 'pressure: %#x, humidity: %#x', i2c_addr, adc_t, + t_fine, adc_p, adc_h) + logger.debug('Sensor Humidity value is %.2f %%', humidity) + else: + logger.debug('BMP280 sensor data: temperature %#02x: %#x = %d, ' + 'pressure: %#x',i2c_addr, adc_t, t_fine, adc_p) + + # convert temperature to right units and scale and return value + temp = args.converter[0](t_fine, I2C_BMX280_SENSOR_SCALE) + logger.debug('Sensor Temperature value %d is %.2f%s', + t_fine, temp, args.converter[1]) + logger.debug('Sensor Pressure value is %.2f hPa', pressure) + return (temp, extra_readings), 1 + except IOError as e: + logger.critical(e) + raise IOError("Error while communicating with I2C device %#02x" % + i2c_addr) + + +########################[ Get I2C MCP9808 Temperature ]######################## +# Inspired by https://github.com/ControlEverythingCommunity/MCP9808 +def read_i2c_mcp9808(args): + """Returns temperature from I2C mcp9808 sensor in desired unit""" + + i2c_bus, i2c_addr = i2c_get_smbus_devaddr(args) + + try: + # Setup MCP9808 configuration and wait 0.5 seconds for things to settle + i2c_bus.write_i2c_block_data(i2c_addr, I2C_MCP9808_CONFIG_ADDR, I2C_MCP9808_CONFIG) - bus.write_byte_data(args.address, I2C_MCP9808_PRECISION_ADDR, + i2c_bus.write_byte_data(i2c_addr, I2C_MCP9808_PRECISION_ADDR, I2C_MCP9808_PRECISION) sleep(0.5) - # Read temperature (0x05), 2 bytes (MSB, LSB) - data = bus.read_i2c_block_data(args.address, I2C_MCP9808_TEMP_ADDR, 2) - logger.debug('Temperature sensor data from MCP9808 %#02x: 0x%02x%02x', - args.address, data[0], data[1]) - - # Convert the data to 13-bits - temp_read = ((data[0] & 0x1F) * 256) + data[1] - if temp_read > 4095 : - temp_read -= 8192 + # Read temperature (2 bytes - MSB,LSB) and convert to 13-bit signed int + data = i2c_bus.read_i2c_block_data(i2c_addr,I2C_MCP9808_TEMP_ADDR,2) + temp_read = ((data[0] & 0xF) << 8) | data[1] + if (data[0] & 0x10): + temp_read = -temp_read + logger.debug('MCP9808 sensor data %#02x: 0x%02x%02x = %d', + i2c_addr, data[0], data[1], temp_read) + # convert temperatur to right units and scale and return value temp = args.converter[0](temp_read, I2C_MCP9808_SENSOR_SCALE) logger.debug('Temperature sensor value %d is %.2f%s', temp_read, temp, args.converter[1]) @@ -166,15 +298,16 @@ def read_i2c_mcp9808_temp(args): except IOError as e: logger.critical(e) raise IOError("Error while communicating with I2C device %#02x" % - args.address) + i2c_addr) +#####################[ Get 1-Wire sensor device filename ]##################### def get_w1_sensor_device_filename(args, dev_dir=W1_SENSOR_DEV_DIR, - prefix=W1_SENSOR_DEV_PREFIX, suffix=W1_SENSOR_DEV_SUFFIX): + prefix=W1_SENSOR_DEV_PREFIX, suffix=W1_SENSOR_DEV_SUFFIX): """Auto-determine sensor datafile name (unless args.file is set)""" - if isempty(args.file): + if not args.file: search_pat = dev_dir + ('/' if dev_dir[-1]!='/' else '') - search_pat+= prefix + '*' if isempty(args.serial) else '*' + args.serial + search_pat+= prefix + ('*' + args.serial if args.serial else '*') logger.debug('looking for sensors with search pattern %s', search_pat) device_folders = glob(search_pat) @@ -196,7 +329,8 @@ def get_w1_sensor_device_filename(args, dev_dir=W1_SENSOR_DEV_DIR, return filename -def read_w1_ds18b20_temp(args): +###################[ Get 1-Wire DS18b20 sensor temperature ]################### +def read_w1_ds18b20(args): """Returns temperature from 1-wire ds18b20 sensor in desired unit""" device_file = get_w1_sensor_device_filename(args) lines=[ '' ] @@ -227,6 +361,7 @@ def read_w1_ds18b20_temp(args): raise ValueError(errmsg) +#######################[ Parse Command Line parameters ]####################### def parse_args(): """Parse command line and get parameters from environment, if present""" @@ -269,18 +404,35 @@ def parse_args(): help='input file (or device) to obtain data from' ' (defaults to %s)' % CPU_SENSOR_DEV) cmdparser = subparser.add_parser('rpi_cpu', parents=[cpuparser], - help='read built-in Raspberry Pi CPU temperature') - cmdparser.set_defaults(func=read_rpi_cpu_temp, cmdparser=cmdparser, retries=0) + help='read built-in Raspberry Pi CPU temperature') + cmdparser.set_defaults(func=read_rpi_cpu_temp,cmdparser=cmdparser,retries=0) i2cparser = ArgumentParser(add_help=False) i2cparser.add_argument('-a', '--address', type=hex_int, help='I2C Address of sensor, use 0x.. for hex (*)') i2cparser.add_argument('-b', '--i2cbus', default=1, type=int, help='I2C Bus to use (defaults to 1)') + + cmdparser = subparser.add_parser('i2c_bme280', parents=[i2cparser], + help='read I2C connected BME280 sensor', + epilog='(*) default I2C address for an BME280 is %#x' % + I2C_BMX280_DEFAULT_ADDR) + cmdparser.set_defaults(func=read_i2c_bmX280, cmdparser=cmdparser, retries=0, + read_humidity=True, default_address=I2C_BMX280_DEFAULT_ADDR) + + cmdparser = subparser.add_parser('i2c_bmp280', parents=[i2cparser], + help='read I2C connected BMP280 sensor', + epilog='(*) default I2C address for an BMP280 is %#x' % + I2C_BMX280_DEFAULT_ADDR) + cmdparser.set_defaults(func=read_i2c_bmX280, cmdparser=cmdparser, retries=0, + read_humidity=False, default_address=I2C_BMX280_DEFAULT_ADDR) + cmdparser = subparser.add_parser('i2c_mcp9808', parents=[i2cparser], help='read I2C connected MCP9808 sensor', - epilog='(*) default I2C address for an MCP9808 is 0x18') - cmdparser.set_defaults(func=read_i2c_mcp9808_temp, cmdparser=cmdparser, retries=0, address=0x18) + epilog='(*) default I2C address for an MCP9808 is %#x' % + I2C_MCP9808_DEFAULT_ADDR) + cmdparser.set_defaults(func=read_i2c_mcp9808, cmdparser=cmdparser, + retries=0, default_address=I2C_MCP9808_DEFAULT_ADDR) w1parser = ArgumentParser(add_help=False) pgroup = w1parser.add_mutually_exclusive_group(required=False) @@ -288,16 +440,17 @@ def parse_args(): help='(unique part of) temperature sensor serial (*)') pgroup.add_argument('-f', '--file', help='input file (or device) to obtain data from (*)') - w1parser.add_argument('-r', '--retries', type=int,default=W1_SENSOR_READ_RETRIES, - help='number of times to retry reading sensor data when' - ' unstable (defaults to %d)' % W1_SENSOR_READ_RETRIES) + w1parser.add_argument('-r', '--retries', type=int, + default=W1_SENSOR_READ_RETRIES, + help='number of times to retry reading sensor data when' + ' unstable (defaults to %d)' % W1_SENSOR_READ_RETRIES) cmdparser = subparser.add_parser('w1_ds18b20', parents=[w1parser], help='read 1-wire connected DS18b20 sensor', epilog='(*) by default the script will look for the first device that ' 'matches %s* in %s, if multiple entries are found -s or -f must ' 'be used to specify which sensor to read.' % (W1_SENSOR_DEV_PREFIX, W1_SENSOR_DEV_DIR)) - cmdparser.set_defaults(func=read_w1_ds18b20_temp, cmdparser=cmdparser) + cmdparser.set_defaults(func=read_w1_ds18b20, cmdparser=cmdparser) # parse arguments and post-process command line options args = parser.parse_args() @@ -306,6 +459,7 @@ def parse_args(): return args +############################[ Exit the Nagios way ]############################ def nagios_exit(status, message, data=None): """exit 'nagios-style', print status and message followed by perf. data""" if logger.isEnabledFor(logging.CRITICAL): @@ -319,6 +473,7 @@ def nagios_exit(status, message, data=None): exit(status[1]) +#################################[ Main logic ]################################# if __name__ == '__main__': try: args = parse_args() @@ -333,11 +488,16 @@ if __name__ == '__main__': temperature, tries = args.func(args) endtime = time() + if isinstance(temperature, tuple): + temperature, extra_data = temperature + else: + extra_data = () + except (KeyboardInterrupt) as e: - nagios_exit(NAGIOS_UNKNOWN,'temperature sensor read aborted by user') + nagios_exit(NAGIOS_UNKNOWN,'sensor read aborted by user') except (IOError, ValueError, ImportError) as e: - nagios_exit(NAGIOS_UNKNOWN,'temperature sensor read failed: %s' % e) + nagios_exit(NAGIOS_UNKNOWN,'sensor read failed: %s' % e) elapse = endtime-starttime logger.info('Got temperature reading of %.2f degrees %s in %fs', @@ -345,6 +505,10 @@ if __name__ == '__main__': unit = args.converter[1] message = 'current temperature is %.2f%s' % (temperature, unit) + data = [ ('temperature', [ '%f%s' % (temperature, unit), + args.warn, args.critical, None, None]), + ('retries', [ tries-1, None, args.retries, 0, None ]), + ('checktime', [ '%fs' % elapse, None, None, 0, None]) ] if args.critical is not None and temperature > args.critical: nagiosresult = NAGIOS_CRITICAL message+= ' and exceeds critical threshold %.2f%s' %(args.critical,unit) @@ -354,10 +518,10 @@ if __name__ == '__main__': else: nagiosresult = NAGIOS_OK - nagios_exit(nagiosresult, message, [ - ('temperature', [ '%f%s' % (temperature, unit), - args.warn, args.critical, None, None]), - ('retries', [ tries-1, None, args.retries, 0, None ]), - ('checktime', [ '%fs' % elapse, None, None, 0, None]) - ]) + for e in extra_data: + message += ', %s is %.2f%s' % (e[0], e[1], e[2]) + data += (e[0], [ '%f%s' % (e[1], e[2]), None, None, + e[3] if len(e)>3 else None, e[4] if len(e)>4 else None ]), + + nagios_exit(nagiosresult, message, data) -- libgit2 0.22.2